Skip to content

Claude/core framework - #3

Merged
BotTony329 merged 10 commits into
developfrom
claude/core-framework
Aug 2, 2026
Merged

Claude/core framework#3
BotTony329 merged 10 commits into
developfrom
claude/core-framework

Conversation

@BotTony329

Copy link
Copy Markdown
Owner

Task

  • Task ID:
  • Task name:
  • Owner: Claude / Codex
  • Objective:
  • Scope:

Implementation

  • Changes:
  • Files changed:
  • Interfaces used:
  • Core files modified: None / list and justify
  • Architecture implications:

Validation

  • Tests added or updated:
  • Commands run:
  • Manual validation:
  • Screenshots or recordings:
  • Known issues:
  • Handoff notes:

Checklist

  • Read AGENTS.md
  • Read AI_HANDOFF.md
  • No direct push to main or develop
  • No unrelated refactoring
  • No balance values scattered as magic numbers
  • Tests added or updated
  • Existing tests pass
  • Documentation updated
  • Frozen interface changes are explicitly documented

claude added 10 commits August 2, 2026 04:56
Adds the Godot 4.3 project shell on top of the existing docs and art pipeline.
No gameplay yet — this is the runnable container everything else sits inside.

- 640x360 viewport upscaled to a 1280x720 window with integer scaling, nearest
  filtering, 2D pixel snapping and MSAA off, so the pixel art in assets/ renders
  without blur or shimmer. This deviates from the 1280x720 viewport suggested in
  godot/import_presets.md, which would draw a 48px sprite at 1:1; see ADR-002.
- Texture importer defaults follow docs/art/GODOT_IMPORT_GUIDE.md (lossless, no
  mipmaps, no 3D detection) and are applied to all 62 PNGs.
- Input map for movement, jump, light attack, confirm, restart and ten debug
  commands.
- Named 2D physics layers so hit/hurt box masks read as words in scenes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
RunState is the authoritative source for run-level player state.

- Fields are private; reads go through getters and writes through named
  commands, so the UI physically cannot poke a stat. Structural values (max HP,
  max stamina, armour, recovery, locks, taxes) are kept apart from momentary
  ones (current HP, current stamina) — the split that stops 'take damage, get a
  better sacrifice, heal, repeat' arbitrage.
- snapshot/restore/clone/snapshot_hash support preview, rollback, logging and
  tests. recalculate_derived() is the single place derived values are produced.
- EventBus carries a uniform payload envelope and is documented as
  observation-only: deterministic resolution uses explicit service calls.
- RNGService gives each subsystem its own stream derived from the run seed via
  FNV-1a, so an extra roll in one system cannot shift another system's sequence.
- BalanceConfig holds every tunable number; data/balance_config.tres carries the
  research report's Appendix A baselines. SoftCaps and DerivedStats keep the
  cap curves and EHP/DPS projections in one place each.
- GameData autoloads the config, the stateless services and the sacrifice
  library. It deliberately does not own the active RunState.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
One damage pipeline, one integrity formula.

CombatResolver implements the bucket order from research report 5.3: base ->
additive -> more -> conditional -> crit -> armour. DamageContext carries every
input so the resolver never reaches into a scene node; DamageResult exposes the
per-stage breakdown so a wrong number can be traced to the bucket that made it.
The crit roll is injectable and a context can pin or force it, which is what
makes crit behaviour testable rather than a coin flip.

IntegrityService implements I = 0.34h + 0.24s + 0.16a + 0.14r + 0.12f over
structural values only. Current HP and current stamina are absent by design.
Each component is capped at 1 so ordinary buffs cannot push integrity above the
intact-body ceiling or launder away a sacrifice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
SacrificeService runs one transaction in a fixed order: validate, snapshot,
apply cost, recalculate integrity, apply reward, recalculate derived, apply
imbalance, publish, return. The reward is priced against integrity *after* the
cost, which is what makes a more broken body buy a sharper blade.

preview() and apply() call the identical _run_transaction; preview runs it on a
clone. There is no second code path, so a preview cannot disagree with the
result. Any failure restores the pre-transaction snapshot whole.

SacrificeDefinition is a Resource with the A8 field groups and a validate() used
by the data test. The one M1 card, 断寿之契 (S2), lives entirely in
data/sacrifices/severed_lifespan.tres: max HP x0.85, +0.18 additive, generic
More x(1+G), +16 imbalance. No card values appear in script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
Player is a CharacterBody2D with movement, jump, light attack, hit reaction,
death and camera follow. It holds no HP of its own: current and maximum
lifespan live in RunState, injected by RunCoordinator, so a sacrifice applied
from the UI is visible to the player with no syncing.

The action states (Grounded, Airborne, Attacking, Hurt, Dead) are one small
class each under states/, with every transition funnelled through
PlayerStateMachine._change_to. Dodge, Exhausted and SameDeath are documented as
extension points with their entry conditions, and deliberately not stubbed.

Hitbox and Hurtbox are the two halves of a damage exchange. A hitbox emits
hit_actor once per actor per swing and knows nothing about damage numbers; the
attacker builds the DamageContext. Hitbox keeps monitoring on and gates on an
active flag, because toggling monitoring mid-frame makes Godot miss overlaps
that started on the same frame.

Art integration: tools/generate_sprite_frames.py emits the SpriteFrames
resources from the frame table in docs/ART_SPEC.md and fails if a sheet's real
dimensions disagree, so the FPS and frame counts exist once rather than in 70
hand-written AtlasTexture blocks. All 62 PNGs verified against the spec; only
assets/effects/*.png disagree (single frame, metadata claims 2-4).

Arena is one fixed 1024x360 space: tiled floor and walls with collision,
three-layer parallax background, props, spawn markers and camera bounds. No
procedural generation — a fixed space is what makes two runs comparable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
EnemyBase is the frozen contract: initialize / acquire_target / change_state /
receive_damage / die, over an IDLE-CHASE-WINDUP-ATTACK-RECOVER-HURT-DEAD state
machine with a readable wind-up telegraph.

die() is idempotent. A second lethal hit in the same frame, a hit after death,
or a direct second die() call all resolve to exactly one death event — there is
a test for each. A corpse also stops running gravity and move_and_slide, since
its body collider is gone and it would otherwise sink through the floor while
the death animation plays.

The reference enemy is 鬼卒 (Ghost Soldier) with the X01 timings: 180px
detection, 65px attack range, 0.40 wind-up / 0.15 active / 0.60 recovery. Every
number is in data/actors/ghost_melee.tres.

BossActor extends EnemyBase rather than introducing a second combat system: it
adds boss-scale stats, its own lifecycle events and a health bar, nothing more.
It ships one melee attack because assets/boss/ contains exactly one attack
sheet; the charge and ground slam are left to Codex X04 rather than faked by
replaying the run cycle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
RunCoordinator drives BOOT -> WAVE -> SACRIFICE -> BOSS -> RESULT -> restart
and owns the RunState for the run's lifetime. Every mutation of the run goes
through it, which is what allows the UI to be a pure observer.

UI: HUD (lifespan, stamina, attack, DPS, EHP, integrity, imbalance, boss health,
phase), sacrifice preview and confirmation, and a shared death/victory screen.
The sacrifice panel previews through SacrificeService — the same transaction on
a clone — and confirms through the coordinator, so it cannot show a number the
apply will not honour. Its visibility follows the run phase rather than its own
button, so it also closes when the sacrifice is applied from debug or a test.

Debug panel (F1) shows seed, phase, player state, full RunState and the
snapshot hash, with commands for heal, damage, spawn enemy, start boss, preview
and apply sacrifice, toggle hitboxes and restart. Every one is a call into
RunCoordinator: a debug button that takes a shortcut hides the bug you are
hunting. Debug damage in particular goes through the normal receive_damage
path so a debug kill exercises real death handling.

DebugShapeOverlay draws hit, hurt and body rectangles itself, because Godot's
built-in collision debug cannot be switched on mid-session.

Main.gd is the composition root: it binds every observer and only then starts
the run, so nothing can miss the opening state_ready.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
52 tests, 205 assertions, all green against Godot 4.3 headless.

Coverage is aimed at the rules that would be expensive to get wrong:

- Combat: bucket order, armour as an equivalent-life model, penetration, forced
  and pinned crits, the crit-rate hard cap and the crit-damage/more soft caps,
  lethality boundaries, and a check that the breakdown multiplies back to the
  final number.
- Integrity: current HP and stamina do not move integrity while max HP does —
  the arbitrage bug the research report was written to fix. Also the intact-body
  ceiling, that a buff cannot offset a structural loss, structural floors, and
  exact snapshot round-tripping.
- Sacrifice: preview leaves the real state byte-identical, apply matches the
  preview it showed, a rejected transaction rolls back whole, the shipped card's
  numbers come from its .tres, the reward is priced against post-cost integrity,
  and a more broken body buys a bigger reward.
- Enemy/Boss: damage intake through the resolver, death fires exactly once
  across four routes, a corpse takes no damage and stops behaving, and the Boss
  reuses EnemyBase rather than a second system.
- RNG: same seed replays, different seeds diverge, and draining one stream does
  not shift another.
- Run loop: the real Main scene is driven through wave, a real hitbox connect,
  sacrifice, boss, victory, restart and death, plus every debug command and a
  smoke check that all ten critical scenes instantiate.

The harness is ~90 lines rather than a vendored plugin, because the Prototype
Development Pack requires the project to test straight after clone with no
extra downloads. If the suite outgrows plain asserts, swap in gdUnit4 rather
than growing the harness.

Local command:
  godot --headless --import
  godot --headless --path . res://tests/test_runner.tscn

The new `godot-tests` workflow runs exactly that and leaves
`repository-validation` untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
The warning line was restating effective HP, which the cost block on the card
already shows. A warning that repeats the numbers next to it trains players to
skip warnings, and the sacrifice contract needs high-risk rule costs to land.

Warnings now cover what the cost block does not: a high warning level, each
rule lock and action tax by name, and the permanent structural loss with its
integrity delta and the reminder that healing cannot undo it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
Documentation now describes the code that exists rather than the code that was
planned.

- ARCHITECTURE.md: directory layout, scene tree, state ownership, the damage
  pipeline, the integrity formula, the sacrifice transaction ordering, the
  events/RNG rules, the player state machine with its extension points, the full
  pixel-art rendering configuration, and an explicit list of architectural gaps
  so absence is not mistaken for oversight.
- INTERFACES.md: the first interface freeze. Every frozen signature and
  guarantee, the data contract, the list of files Codex must not modify, and the
  extension points that are open for work today.
- DECISIONS.md: eleven ADRs, including deriving M1 from the Development Pack
  while leaving the PRD to the Game Director, the viewport deviation from
  import_presets.md, authored imbalance winning over the derived formula, the
  no-second-damage-path invariant that gives the rollback a real trigger, and
  why the Boss ships one attack.
- TESTING.md: how to run the suite, why it runs as a scene rather than via
  --script, what is covered, and an honest list of what is not.
- AI_HANDOFF.md: branch, playable status, implemented systems, frozen
  interfaces, protected files, the eight Codex tasks with their real blockers,
  known issues, test status, and a per-directory asset integration audit.
- TASKS/README.md: C01-C09 marked done, X01-X08 marked ready or blocked with
  reasons, plus the framework work still owned by Claude.
- README.md: how to launch and play, controls, debug keys, test commands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jo4LHdYmCJBrqzgB5j8KGa
@BotTony329
BotTony329 merged commit efbb4c1 into develop Aug 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants