332 Commits

Author SHA1 Message Date
kronic 5dced78de2 Docs: 2026-08-13 — Track A (camera) shipped alongside Track B
Session log gains Part 3: the operator picked "drop" on the hold fork, and both
halves of the camera fix landed. Records the measurement that forced the second
half — dropping the hold ALONE was worse (max deviation 0.653 vs 0.447) because the
hold had been masking the shake-integration bug — plus the before/after table and
the PixelArtDevControls opt-in.

Gotchas archive gains four more entries: never let a transient offset live in the
value your smoothing filter reads back; a frame-counted hold is a framerate-
dependent freeze; a masking fix can make the metric worse before better, so
re-measure the intermediate state; HideFlags.DontSave objects survive play-mode
exit and leak one per domain reload.

CLAUDE.md gains a Camera-feel ★ line, paid for by trimming the MCP-edit, swept-hit,
LANTERN-direction, harvest, bootstrap and presentation bullets. Now 39936 bytes —
exactly the >=1 KB headroom target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:09:53 -07:00
kronic 150f30f56c Fix: PixelArtDevControls is opt-in — it was running OnGUI in Game.unity every frame
The editor-only pixel-art tuner self-spawned via [RuntimeInitializeOnLoadMethod] +
DontDestroyOnLoad into EVERY scene, not just DevSandbox, and its OnGUI draws the
toggle button on every IMGUI pass BEFORE the `!_open` early-out. IMGUI dispatches
at least Layout+Repaint per frame, so it allocated continuously during normal
gameplay — it is a MonoBehaviour, so an ECS system-toggle sweep never touched it,
and it dominated this session's allocation profiling until it was found.

Now gated behind an EditorPrefs opt-in with a checked menu toggle, mirroring
`ProjectM/Boot Into Menu (Editor)`. Off by default.

Separately: because the object carries HideFlags.DontSave it SURVIVES play-mode
exit while the static _instance does not, so the old code leaked one instance per
domain reload — two live strays were found and cleared in the editor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:03:15 -07:00
kronic 4bd00818e1 Fix: the combat "freeze on a kill" — camera shake channel + drop the position hold
This is what the operator's report was actually describing, and it was never a CPU
hitch: median frame was 12.2 ms with a kill costing ~15 ms, wave respawn is not a
spike, and enemies carry no PhysicsCollider so corpses never blocked.

Two bugs in PrototypeCameraRig.LateUpdate, both measured with a STATIONARY player
so the ideal camera motion is exactly zero and everything observed is artifact:

1. Shake was integrated by the follow filter. The smoothing ran as
   Lerp(transform.position, desired, k), and transform.position already contained
   last frame's random shake — so the filter treated shake as real positional error
   and corrected only ~9 %/frame. Shake was never subtracted, just slowly lerped out
   while new shake piled on, so in sustained combat the camera random-walked around
   its framing and never settled. One kill left it 0.447 units off-frame and still
   0.199 off 24 frames later. The follow now smooths a _basePos that shake never
   touches; shake is applied only when writing the transform.

2. The position HOLD froze the follow for a fixed number of RENDER frames on every
   kill and heavy hit (2, or 7 on a finisher), making the freeze framerate-dependent
   — 49 ms at 144 fps, 233 ms at 30 fps — and it took its base from
   transform.position, permanently baking that frame's shake in as a 0.283-unit
   single-frame jump. A camera that stops while the world keeps moving reads as a
   hitch, not as crunch. Dropped; impact rides the FOV punch + shake alone
   (operator's call). HitStopMaxFrames / HitStopFreezeEnabled / FinisherHoldFrames
   retire with it, and the three saved feel profiles drop the dead keys.

Worth recording: dropping the hold ALONE measured slightly worse (max deviation
0.653 vs 0.447) — the hold had been partly masking the shake integration. Both
fixes together: deviation returns to 0.000 within a few frames of a kill, mean
deviation 0.038 across the window. The 0.428 peak is now the shake impulse itself,
a transient punch that immediately returns instead of a drift that lingers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:03:06 -07:00
kronic e0c59ad663 Perf: pool one-shot SFX + authored VFX, cut per-frame presentation allocation
Track B. All 21 one-shot cues funnelled through FeedbackFx.PlayClip ->
AudioSource.PlayClipAtPoint, which allocates a GameObject + AudioSource per call
and schedules a delayed Destroy — ~20-33 times a second in light combat. New
OneShotAudioPool is a 32-voice 3D ring behind an UNCHANGED PlayClip signature, so
all 20 consuming call sites are untouched.

Parity is the whole game here: PlayClipAtPoint sets spatialBlend = 1 explicitly (a
fresh AudioSource is 2D) and leaves the rest at stock defaults. Two deliberate
divergences, both forced by the voices being long-lived: playOnAwake = false, and
dopplerLevel = 0 because a pooled voice TELEPORTS between events and would
otherwise pitch-bend. Root is DontDestroyOnLoad (WorldLauncher does
LoadScene(Single) while the client world is alive) with a SubsystemRegistration
reset, or session two rents destroyed voices.

Authored impact VFX are pooled per prefab instead of Instantiate/Destroy per hit:
components cached per INSTANCE (refs are instance-scoped), main.stopAction forced
to None (a prefab set to Destroy silently drains the pool), instances filled under
an inactive root so Awake/Start never run — which is what makes the DestroyImmediate
in StripCosmetic safe — ps.Clear before Play, TrailRenderer.Clear after the
reposition, and a Rented flag as the at-most-once guard against a double Return
aliasing one instance to two callers.

Per-frame allocation: the slash-arc and enemy-wedge mesh builders each allocated
four arrays on every call (up to twice a frame, and once per winding enemy); HUD
and ability-bar labels rebuilt their strings every frame; damage-number fades
rewrote TextMesh vertex colours every frame; health bars pushed uGUI writes
unconditionally; two systems played back an empty EntityCommandBuffer (a
structural-change sync point) every frame.

Also closes an AudioClip leak across all seven clip-owning systems: an
AudioClip.Create'd clip is a standalone UnityEngine.Object, so destroying a
system's FX root left it alive (MusicSystem ~6.8 MB, AmbientAudioSystem ~2 MB per
client-world teardown).

CombatFeedbackSystem's TryHold call sites go with this commit because they share
the file; the camera-side removal lands in the next one.

Verified live: PlayClipAtPoint's "One shot audio" GameObject never appears again
across 270 frames of combat with kills; the VFX pool fills to its retain cap and
stabilises; real cues route through the ring. 304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 23:02:50 -07:00
kronic bdeee3c51a Docs: 2026-08-13 session — combat freeze diagnosis + Track B allocation pass
Session log for the "freezes on a kill" diagnosis (the cause is the camera, not
CPU: shake is integrated by the follow filter, the hold is frame-counted, and the
hit + kill packages stack on the lethal frame) and for the Track B allocation work
the operator selected.

Gotchas archive gains nine dated entries, incl. two that changed how the work was
ranked: GC.GetTotalMemory quantises to 4 KB and is useless for allocation
attribution (use ProfilerRecorder), and an editor-only IMGUI dev tool self-spawns
into Game.unity and dominated every editor-side measurement.

CLAUDE.md: one new pooling/per-frame-cost line, paid for under the net-zero rule by
dropping the DR-051 build-palette text (that code died with the purge), the two
purge enumerations now carried by their DRs, and a duplicated ACES clause.
40183 bytes, under the 40960 budget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 22:39:30 -07:00
kronic 13d591e789 Fix: revive the walk-through flora rustle — a fourth orphaned presentation layer
Track 1. Before building any new "world responds to you" work, I checked what
already existed, because today's score was three presentation systems that were
present, enabled, and produced nothing. This is the fourth.

AmbientMotionSystem's rustle layer (flora parting as the local player brushes
past) was dead in the shipping arena via THREE independent gates, each a
leftover of the deleted biomes:

  1. Root gate required a scene root literally named "BaseBiome" or
     "ExpeditionBiome*". Both went with the expedition, so the loop matched
     nothing at all. The seabed flora live under
     Env_SeabedKit/Seabed_Flora/FloraClump_N.
  2. Name regex was pure LAND flora: bush|grass|flower|fern|cactif|clover...
     Nothing in a seabed matches that. Added seabed terms first; the land terms
     stay only so an older scene still works.
  3. Renderer gate tested for a renderer ON the matched object. The natural sway
     unit is the CLUMP ROOT, which carries no renderer of its own - only
     children - so the test rejected exactly the object we want to rotate. Now
     accepts a renderer anywhere beneath, and skips a child whose parent already
     matched so the clump tilts once instead of every stalk tilting out of its
     socket.

Verified live: AmbientMotionSystem._flora.Count 0 -> 4 (the four clump roots).

304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:50:13 -07:00
kronic b00926dc6e Art: seabed relief kit — 5 modular pieces, built in Blender and baked into Game.unity
Track 2 of the A0 readability work. The operator wanted non-flat ground; the
walkable surface must stay flat, because the character controller is configured
for a plane (SnapToGround=false, gravity float3.zero) and enemies have no
pathfinding. Real elevation is a locomotion + navigation task, not an art one.

The kit therefore avoids the height band that breaks entirely:

  SM_Seabed_Hollow_A   442 tris  below plane   no collider
  SM_Seabed_Dunes_A    442 tris  +/-0.4 crests no collider
  SM_Seabed_Ridge_A     55 tris  ~1.2 m        rim placement
  SM_Seabed_Outcrop_A  120 tris  ~1.4 m        rim placement
  SM_Seabed_Shelf_A     96 tris  ~1.4 m        rim placement

1155 tris total, all far under the <=800/prop budget, faceted, one material.
Nothing sits at 0.3-0.6 m, which is the range that either clips the diver's legs
(no collider) or becomes a wall you cannot see over (collider). The three tall
pieces sit at the arena RIM outside the 16u wave ring, where the boundary
already blocks the player - so they read as silhouette with no subscene collider
work and zero nav risk.

Two art lessons, both corrected mid-build:
  - First pass encoded relief as ALBEDO BANDING from the palette atlas. At
    top-down, flat colour bands read as a printed pattern, not form: the hollow
    looked like a bullseye and the ripple sheet like woven basketwork. The style
    bible is explicit that the read is shape + light. Uniform albedo + real
    geometry + faceted normals doing the shading.
  - Cutting an organic outline by dropping whole quads gives stair-stepped
    edges. Rebuilt in POLAR coordinates so the boundary follows R(theta) exactly.

In-engine: the palette grey read ~2x brighter than the seabed floor, so the
pieces looked like slabs laid on top. Ground pieces now share the floor's own
M_Staging_Seabed; rock pieces use a darker sibling M_Seabed_Rock (0.13,0.19,0.23)
so they read as the same ground family, a shade separated.

Exported per-piece so the Phase-2 pocket generator can place them individually
rather than inheriting a bespoke landscape.

304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:46:57 -07:00
kronic 1835718023 Docs: 2026-08-08 session log — A0 gate, readability pass, arena regression, seabed relief kit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:32:50 -07:00
kronic 9f145e3a83 Art: ArenaFieldSystem placement policy — props in the fought-in band, nav-safe spacing
Operator delegated the placement call. Applied standard top-down-arena practice
rather than the first-pass ring scatter:

  clear core (5u)      the landing spot and dash room; props there cramp spawn
                       and cause dash collisions the player never asked for
  band 6-13u           solids live in KITING range, which is also roughly where
                       the diver's travelling light reaches. A prop outside the
                       lit radius reads as an invisible wall - the worst failure
                       mode in a dark game, so cover is pulled inward to where
                       it can be SEEN before it is hit
  min spacing 2.6u     enforced across clutter AND cover on ONE shared occupancy
                       list. This is a NAVIGATION guarantee, not an aesthetic:
                       enemies have no pathfinding, so gaps must stay wider than
                       a body. Rejection-sampled; a piece that cannot fit is
                       DROPPED rather than jammed into a gap movers can't pass
  explosives >8u       a barrel near the landing spot chains into you on spawn

Measured live, all three invariants hold:
  17 solids, radius 7.89..12.95, min gap 2.79 (policy >=2.60), 0 explosives <8u

Nav re-validated on a CLEAN run: closest approach 1.40u, two enemies in melee.
Enemies cross the inward cover band and reach the player.

Correction worth recording: three intermediate readings showed "frozen movers"
and were WRONG. Teleporting the player around a long-running Play session
desyncs the predicted character controller and leaves stale AI state - the
server had the player back at (2.5,0) while I was measuring against (0,0), and
an earlier "all four frozen" reading was simply enemies that had converged and
were attacking. Only the clean, unmutated run is valid. Do not mutate server
state and then trust a movement measurement in the same session.

304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:26:42 -07:00
kronic 05bf835862 Fix: ArenaFieldSystem — restore the arena's clutter, cover and geysers (purge regression)
Operator at the A0 gate: "the world is very static." Part of that was a
regression from today's purge, and the audit's own signature pattern for the
third time: the system exists, the authoring is placed, and it silently
produces nothing.

Server/Economy/RoomFieldSystem.cs was deleted with the run/room FSM in
62e48a3b0. That was correct for the FSM, but the system was also the only
consumer of the ClutterFieldSpawner / CoverFieldSpawner / GeyserFieldSpawner
singletons. Those kept baking into Gameplay.unity and nothing read them:

  before:  Geyser 0   BlightClutter 0   (spawners configured for 14 + 2 + 3)
  after:   Geyser 2   BlightClutter 17

So the arena lost 14 destructible clutter pieces, 3 cover pieces and 2
periodically erupting geysers — a large share of everything that moved or
could be interacted with.

The old system could not be revived: it keyed every spawn off RunInfo,
RunRuntime, RoomPlan, RunMapMath, RegionMath and RoomTag teardown, all gone.
ArenaFieldSystem is a gym-scoped replacement that seeds ONCE at world start
and never tears down — Phase 1 is "no world, a gym", and everything
room-shaped belongs to the Phase-2 pocket generator, not here.

Contracts carried over deliberately, each load-bearing:
  - baked.WithPosition, never FromPosition (FromPosition resets Scale, a GhostField)
  - Geyser NextEruptTick born-correct off the live ServerTick via TickUtil.NonZero,
    staggered 60 ticks per instance so eruptions desync; 0 stays "not ready"
  - a 6 u keep-out ring around the arena origin (the player's landing spot)
  - clutter variant 3 = the explosive hazard, 0-2 inert dressing
  - a fixed arena seed: a gym wants the same arena every session

Verified live: geysers stamped 306 / 366 (staggered, Scale 1.00 preserved).

Navigation re-validated per the standing rule that adding Environment cover
can freeze movers, since enemies have NO pathfinding. First measurement showed
all four "frozen" — that was my error: they had already converged and were
attacking. Re-tested by moving the player across the arena: all four closed
23.5->20.4, 24.2->21.2, 23.9->20.9, 22.8->19.7 u through the cover field. No
soft-lock.

304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:15:26 -07:00
kronic 501c52d809 Art: A0 readability pass — murk plane, seabed albedo, and a suit glow
Operator at the A0 gate: "a little too dark to play, not very readable yet."
Measured rather than guessed, and the diagnosis was NOT "add light":

  seabed _BaseColor (0.08,0.12,0.15) — the floor was painted near-black, so it
    reflected ~10% of anything and could never read as a surface
  fog colour (0.02,0.10,0.12) — distance faded to VOID rather than to murk;
    real water scatters, so far things should get lighter and lower-contrast
  ColorAdjustments contrast +10 on top of ACES — the low end was crushed twice
  Vignette 0.28 — ate the periphery, which is where threats arrive from

Murk pass (operator picked "murk + closer camera"):
  BaseFogColor/ExpeditionFogColor -> (0.06,0.20,0.25)   [WorldAtmosphereConfig
    drives RenderSettings every frame, so the CONFIG is the real knob]
  M_Staging_Seabed albedo -> (0.18,0.26,0.30)
  contrast 10 -> 4, vignette 0.28 -> 0.16
  camera Distance 17 -> 14

That fixed the void but INVERTED figure-ground, which the numbers caught:

  player 0.144 | rock 0.269 | flora 0.258 | warm haze 0.368

The protagonist was the second-darkest thing on screen. The shoulder lamp is a
forward-facing spot tilted 26 deg down — it lights the seabed ahead and never
its own wearer — so the diver had no personal light at all. Raising KeyWarm
would have lifted the scenery by the same amount and left the ratio unchanged;
only a light that TRAVELS with the diver changes it. Added ~SuitGlow: a warm,
steady point light at torso height (FeelConfig.SuitGlowIntensity 5 /
SuitGlowRange 4.5, live-tunable, 0 = off). "Light is territory", made literal.

  player 0.144 -> 0.320, now above rock 0.282 and flora 0.250
  figure-to-murk contrast 1.32x -> 2.94x, with scene brightness unchanged

304/304 EditMode green. Awaiting the operator's eyes on the result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 16:58:52 -07:00
kronic baa6eb521a Docs: CLAUDE.md — stale-refresh + no-still-catches-a-temporal-bug rules (net-zero paid)
Two new MCP-workflow rules earned the hard way during the A0 gate. Paid for by
condensing the enemy-slide history and the LANTERN direction bullet into the
gotchas archive, which also records the full A0-gate lessons.

Corrected while there: the direction bullet still said the engine fork was OPEN
and must be surfaced before world code. DR-053 closed that on 2026-08-07 —
PARKED, Unity stays.

39850 bytes, 1110 headroom.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:13:28 -07:00
kronic 34b41532ee Art: Game.unity AmbientMotionConfig drift 28/0.09 -> 18/0.07
The serialized component keeps its own values, so changing the C# field
initialisers in the previous commit never reached the shipping scene (the
standing 'a serialized field ignores the C# initializer' rule). Colour was
already fixed in code, so only rate and size were still stale.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:10:17 -07:00
kronic de81a6f2f3 Fix: StagingAmbiance must never drive a rig-owned camera (zoom/shake regression)
Self-inflicted, found by the operator the moment they pressed Play: "the camera
is so far zoomed in and shaking".

StagingAmbiance rides Env_SeabedKit.prefab, which Game.unity instantiates as
well as ArtStaging — a placement that predates this session and was harmless
while the component only pulsed flora. The camera lissajous drift added in
b407f7cd6 then wrote _cam.position every LateUpdate against
PrototypeCameraRig's follow, pinning the camera to the authored _camBase
(0, 1, -10) while the rig lerped toward its desired (2.50, 16.72, -9.01).

Measured before: camera y oscillating 1.157 <-> 13.559, max 7.43 units per tick.
Measured after:  y settles 16.658..16.709 (target 16.72), span 0.051, max step
0.004 — a ~1800x reduction in per-tick jitter.

Gate on the presence of PrototypeCameraRig, not on a scene name (scene-name
checks are banned by CLAUDE.md). Verified both directions: ArtStaging has no rig
so its drift still runs; Game.unity has one so the drift is correctly inert.

Note for next time: two `refresh_unity scope=scripts` calls reported
compile_requested=false and the editor kept running the STALE assembly, so the
first verification pass showed the guard "not working" when it simply was not
loaded. scope=all mode=force was required. Confirm a behavioural fix by probing
runtime state (_cam == null), not by re-reading the source.

304/304 EditMode green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:09:41 -07:00
kronic b407f7cd6f Art/Fix: A0 gate pass — death-pose lock, off-palette drift, lighting debris
Found by running the A0 style-proof against the SHIPPING scene instead of
ArtStaging (which judges static glTF bakes on an import shader, with zero
SkinnedMeshRenderers and no Animator — it cannot answer "does the game look
like this").

Player-facing bugs fixed:

- AC_PlayerTopDown/AC_EnemyTopDown "Death" had ZERO outgoing transitions.
  Any State enters on IsDead and nothing ever leaves. The player ghost is a
  persistent entity, so its Rukhanka animator never resets: once you died you
  rendered sprawled on the seabed permanently — sim fully respawned, moving and
  fighting, visually a corpse. Added Death -> Idle on (IsDead == false).

- AmbientMotionSystem drift was still the deleted biome system: key 0 =
  "meadow", MeadowMoteColor (0.62, 0.85, 0.60) = GREEN motes, 600 of them in a
  36x7x24 box following the camera — the most off-palette thing in the frame.
  Its x>500 region probe pointed at space now holding zero renderers. Collapsed
  to one cold marine particulate; dropped the 4 dead mote colours + AridWind.

- FeedbackFx.MakeParticleMaterial built Sprites/Default with NO texture, so
  every procedural particle rendered as a hard SQUARE. Added a procedural soft
  dot (no Resources.Load — build-stripped; asset-free presentation is the rule).

- Game.unity carried a stock Unity "Directional Light" (warm-WHITE, 1.05, Soft)
  outranking KeyWarm as a second competing shadow caster, plus 6 orphaned
  LandmarkLights — four at x=1030/1530 lighting a kilometre of empty water, two
  with no renderer within 12u, two of them purple. All 7 deleted. Added RimCold
  for silhouette separation; key:fill set to the tuned 3.9:1; WarmPool -> Soft.

ArtStaging (kept as a lighting lab): KeyWarm 0.15 -> 0.85 — it was the only
shadow-caster yet sat BELOW GloamFill 0.22, which destroys form by definition
and is what read as "flat". StagingAmbiance was attached to nothing and its
flora wiring searched for a Game.unity parent, which is what read as "static";
it also force-played the combat one-shot pool, now restricted to looping
emitters. Flicker raised to a measured x2.11 cold swing vs x1.03 warm.

304/304 EditMode green; death-recovery, palette and particulate all verified
live in Game.unity Play.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:45:18 -07:00
kronic c87dd04e87 Docs: truth pass over CLAUDE.md, DRs and the MOCs (audit M13/M14/M15/M16/M19/L11/L12)
CLAUDE.md — it is loaded every session, so its errors cost the most:
- M13: it still said "No world code until the world-model spike passes
  its design review" while DR-049 has been `accepted` since 07-13 and the
  roadmap says PASSED. A hard contradiction between the two authority
  docs, with CLAUDE.md winning by being always-loaded — plausibly part of
  why Phase 2 never started. Now points at the build spec, warns it needs
  re-anchoring, and links the parked engine fork.
- Records the H2 hazard as a rule: a socket whose SparkId is missing from
  the baked AbilityDatabase silently reads Damage/Range/Cooldown 0.
- L12: the asmdef table was stale on all four rows; also notes the fifth
  compilation unit (Scripts/Editor has no asmdef) and that tests now
  reference Authoring.
- M19: per-machine setup — the machine-loss recovery checklist — was
  missing Blender + blendermcp (which the committed /art-dev skill
  hard-requires) and ctx7, and claimed a ${CLAUDE_PROJECT_DIR} in
  .mcp.json that isn't there.
- Structures / harvest / persistence / BefourStudios bullets rewritten:
  they described deleted subsystems.
- Net-zero rule honoured: the additions pushed it to 41,057 (over the
  40,960 hard limit), so two long art-pipeline bullets moved verbatim to
  the gotchas archive under a dated heading. 39,661 now — inside the
  file's own 39,936 soft ceiling.

- M14: DR-053 files the Bevy fork as PARKED. The guide was reachable from
  NOTHING — one file, zero inbound links, and its gating instruction
  lived only in machine-local memory, which CLAUDE.md explicitly forbids
  as a sole home. It now has a committed home and inbound links.
- DR-054 records this purge, including the three files that were in the
  delete set until their consumers were checked and turned out to be
  load-bearing.
- M15: 13 superseded DRs re-statused with `superseded_by`. Only DR-002
  carried the field before, though the project's own template prescribes
  it.
- M19: Home.md stated three wrong facts (latest DR, latest session, date)
  — refreshed. Pillars.md and Systems_Index.md now carry warning banners:
  both assert things the purges falsified, and Systems_Index still claims
  to be "the accurate map of what exists in code today".
- L11: the 4 genuinely-broken wikilinks fixed (typo'd DR filenames). Zero
  broken vault-internal links now.
- Session log written — the last three commits before today had none,
  breaking the protocol's own bookend rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:44:18 -07:00
kronic e42760e7d0 Repo: reclaim 8.5 GB of unreachable vendor content (audit L7/L8/M13)
Assets/ 14 GB -> 5.5 GB.

- Assets/BefourStudios DELETED (8.2 GB). Transitive-closure reachability
  from every project scene, prefab and settings asset found exactly FOUR
  reachable files in the whole pack — four textures. Relocated them to
  Assets/_Project/Textures/Env via AssetDatabase.MoveAsset with the GUIDs
  verified preserved, then removed the pack. It was HDRP art for a
  direction DR-051 deleted; re-downloadable from the Asset Store.
  (3.3 GB of that was gitignored .unitypackage archives git rm could not
  see — removed separately.)
- 92 vendor demo/gallery/overview scenes deleted (294 MB). They are the
  ten largest tracked blobs in the repo, stored as non-LFS YAML that
  Unity must parse and index on every database rebuild. Build settings
  reference only MainMenu and Game.
- com.shadercrew.the-toon-shader.3d + .core removed (132 MB embedded,
  referenced by nothing).
- Assets/Screenshots (182 PNGs, 22 MB) moved to the gitignored
  _visual_scratch/ — Unity was importing them as textures.
- manifest: dropped com.unity.ai.navigation and com.unity.cinemachine
  (zero references — no NavMesh assets, no Cinemachine in any scene).
  Pinned com.coplaydev.unity-mcp to a SHA instead of #main; that floating
  branch ref silently re-resolving IS what the dirty packages-lock.json
  was, and it means two machines can run different bridge versions while
  both match the committed manifest.

KEPT after checking, against the audit's suggestion: visualeffectgraph
(27 .vfx assets) and timeline (213 .playable) are genuinely used. And I
RESTORED com.unity.ai.assistant + com.unity.ai.inference after removing
them left half the AI Toolkit resolved in Library/PackageCache and
crashed two AssetImportWorkers on domain reload — editor-only tooling is
not worth destabilising the install for.

Verified after: 0 compile errors, 304/304 green, DevSandbox plays with
the player, an enemy, live socket stats and no leaked RPCs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:39:13 -07:00
kronic 0744cdf4f3 Art: EmissiveGloam DOTS instancing + revive EnemyRigTools + drop dead materials (audit M7/M8)
M8 — ProjectM/EmissiveGloam had no DOTS instancing but is bound to
M_Drowner_EyeGlow on EnemyDrowner.prefab, a baked GHOST prefab that
Entities Graphics renders through BatchRendererGroup. Its skinned sibling
has carried the pragmas since 07-16; this one was simply missed, and murk
hides the symptom (CLAUDE.md: "a dark-lit screenshot MASKS material
bugs"). Added #pragma target 4.5 + multi_compile DOTS_INSTANCING_ON +
UNITY_SETUP_INSTANCE_ID, and a DepthOnly pass so the glows stop being
absent from the URP depth texture (soft particles / SSAO / DoF punch
straight through them otherwise). Verified: shader compiles, isSupported,
2 passes, 0 messages.

M7 — EnemyRigTools was dead at EVERY entry point. All eight hard-coded
prefab paths were deleted by DR-051, and the template check ran BEFORE
the idempotent in-place branch, so even re-running a builder on an
existing prefab failed. The three shipping skinned creatures were
unreproducible. Now the template only matters when there is no output to
edit in place, and a new creature templates off the shipping EnemyDrowner
ghost (GhostTemplate) instead of the deleted Enemy.prefab. Retired the
four menu items that drove the deleted Werewolf/Kaiju/Charger bestiary
rather than leave them advertising entry points that cannot work.

Deleted 19 materials with zero references from any scene, prefab,
ScriptableObject, script or settings asset — verified three ways, not
from the audit's list. Kept everything with a live reference, including
several the audit's asset-only scan called dead but which code loads by
path (M_Skinned_Palette, PixelOutline, M_Env_Fallback).

NOT fixed here, deliberately: MakeMat still points each creature at a
per-PACK Synty atlas rather than the shared palette. That is a visual
decision about the shipping Drowner/Grindylow, not a mechanical fix — the
doc-vs-reality gap gets reconciled in Art_Direction_Lantern instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:30:43 -07:00
kronic b80aa1b24b Tests: tuning invariants + Authoring reachable from the test asmdef (audit M10/M11/L18)
- New TuningInvariantTests (6 tests). Two gaps the audit named:
  * The six-site knob drift: adding a knob touches the struct field,
    Defaults(), the TuningKnob const and three switches, and nothing
    caught a knob wired into Apply but forgotten in ClampKnob. The clamp
    is the only thing stopping a 0 reaching the i-frame divide, which
    TuningConfig's own header records as "NaNs the kinematic body
    permanently". Now every LIVE knob round-trips Apply -> Get against
    ClampKnob.
    The knob set is read by reflection, not assumed to be 0..Count:
    CLAUDE.md reserves retired indices (20-23), so the space has holes
    on purpose. A second test pins that those stay reserved — re-using
    one would silently re-mean a value in saved feel profiles and in the
    DebugOp wire payload.
  * MeleeTiming had ZERO test references while producing the locked
    20/12/25 contact ticks, and MeleeComboTests pins fixture values that
    differ from the shipped defaults on nine of ten knobs. The new tests
    assert RELATIONSHIPS against Defaults() — finisher contact stays
    inside both CastFacingTicks and MeleeRecoverTicks — so a deliberate
    retune stays green and only a broken coupling fails. That coupling
    was previously enforced only by a [MenuItem] a human had to remember
    to run.
- Test asmdef now references ProjectM.Authoring + Unity.Entities.Hybrid,
  so bakers are reachable from tests at all (L18). Nothing referenced
  them before, which is why "no prefab carries ChargerAuthoring" could
  coexist with a green suite.

CORRECTION to the audit: "10 test files leak ECS Worlds" is REFUTED.
That finding came from counting `.Dispose()` tokens, which misses the
`using (world)` blocks these files actually use. Measured empirically:
World.All is 6 before the suite and 6 after — zero leaked.

304/304 green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:24:50 -07:00
kronic 37b211a7f8 Netcode: fix interpolated-tick cues + add a terminal RPC reaper (audit M1/M4/M12)
M1 — two systems timed INTERPOLATED ghosts against the PREDICTED tick,
the exact hazard CLAUDE.md documents and the one that is invisible on
loopback:
- EnemyDangerTelegraphSystem timed the red danger cone off nt.ServerTick,
  so over a real connection the dodge tell finished ~RTT/2 + interp
  buffer EARLY. The cue lied.
- PlayerAnimationDriveSystem fed the same predicted tick to RemoteDriveJob
  ([WithDisabled(GhostOwnerIsLocal)] — i.e. interpolated teammates), so a
  teammate's swing animation desynced from their damage.
Both now use the ZoneTelegraphSystem idiom. The LOCAL drive job keeps
ServerTick: the owning player really is predicted.

M12 — the RPC leak I reproduced live during the audit. Every receiver in
this project gates on RequireForUpdate over a scene-baked singleton; in a
scene without it the receiver never runs and the request entity is never
destroyed. Netcode's WarnAboutStaleRpcSystem Consume()s but never
destroys, and is compiled out of player builds — so these accumulated
silently, and worst in a shipped build.
New StaleRpcReaperSystem (server, OrderLast, no RequireForUpdate) destroys
any unconsumed request that outlived its receiving frame. Consumed
requests are left to their owner. Verified live: a planted unconsumed
request is gone within a few frames. Three regression tests pin both
halves of the contract.

Also: HealthApplyDamageSystem and ProjectileDamageSystem now filter
.WithAll<Simulate>(). They are ServerSimulation-only so it was not a bug,
but the audit's "all predicted systems filter Simulate" reassurance was
false until now — the rule is unconditional again.

The five undisposed Allocator.Temp ECBs the audit flagged all lived in
systems the purge deleted; none remain.

298/298 green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:17:47 -07:00
kronic 9f61f7c6fe LANTERN kit into the shipping scene + frame rename (audit H2/M4)
H2 was the audit's sharpest finding: in Game.unity every player spawned
with four ability sockets pointing at SparkIds the baked AbilityDatabase
did not contain, so all four resolved to Damage=0 Range=0 Cooldown=0.
Melee and dash were the only working combat verbs in the built game.
Cause: the 5 LANTERN Sparks were added to GymSub.unity and never to
Gameplay.unity.

- Gameplay.unity's AbilityDatabaseAuthoring now carries all 9 defs (the
  4 legacy ids keep their numbers; Sparks are 5-9) with their effect
  prefabs. Live-verified in Play: sockets now read
  Vortex 8dmg/6range/420cd, Blink 20range/1cd, Hook & Pull
  15dmg/25range/120cd, Light Zone 6dmg/5range/480cd.
- Removed 6 orphaned authoring GameObjects the purge left behind in the
  subscene (StorageSpawner, StructureCatalog, ItemDatabase,
  SpitterProjectileConfig, BoonCatalog, MetaCatalog) and the RoomDressing
  object in Game.unity — the latter is what scattered 47 Synty desert
  props into the seabed murk.
- Deleted 4 now-unreferenced prefabs: EnemySpit, Pylon, Storage, Wall.
- Frame rename (M4): FrameKind.Warrior/Ranger -> Bathynaut/Harpooner,
  93 identifier sites. Byte values pinned (2/3), so no ghost-hash or save
  impact. The menu said "Warrior"/"Ranger" to players three weeks after
  the frames were renamed in design.
- Menu now reads LANTERN / "Light is territory — co-op descent" instead
  of "PROJECT M" / "Frontier colony — co-op" (the Awakening-Engine
  tagline, two directions stale).
- Removed the "Replay Tutorial" button and the Settings ONBOARDING
  section: both drove coach-marks DR-051 deleted. The settings fields
  still round-trip so existing settings files load unchanged.

All four project scenes + all prefabs verified free of missing scripts.
Server measured at 59.6 ticks/s against a 60 Hz target. 295/295 green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:11:58 -07:00
kronic 62e48a3b0b LANTERN purge: delete the superseded base/expedition shell (audit H1/H3/M5)
The 2026-08-06 audit found the shipping scene was still the abandoned
co-op-Hades game with LANTERN combat bolted on, and that a third of the
codebase was live code for a direction abandoned on 2026-07-13. Operator
chose deletion over freezing: "everything is saved in source control if
needed. I want the project to be clean."

DELETED (~140 source files, Scripts 335->231, Tests 77->43):
- Enemy variants + boss (H3). ChargerAuthoring / SpitterAuthoring /
  SwarmerAuthoring were attached to ZERO prefabs, so LungeState /
  SpitterState / SwarmerTag were never baked: ~272 lines of Bursted AI
  passes, BossAISystem (261 lines) and the whole MixBands escalation
  curve could not match a single chunk at runtime, while 734 lines of
  green tests certified them. Both shipping enemy prefabs were already
  byte-identical in stats.
- Run/room lifecycle: RunDirector FSM, RunInfo/RunMap/RoomPlan/RoomTag,
  route select, portal interact, ready-check, room field/teardown.
- Meta shop, prep loadout, boons (incl. KillRewardSystem and
  DashTrailDamageSystem, which existed only to serve boon flags).
- Build palette + structures, shared storage, inventory/equipment
  (already recorded PAUSED in CLAUDE.md).
- The HUD panels driving all of the above (HudSystem 1168 -> 610).

KEPT deliberately: BaseGridMath + BaseAnchor (8 systems use PlotCenter
for spawn rings, respawn and dynamic light), the resource ledger +
StorageMath, the save system, region/relevancy. Three of these were in
the delete set until I checked their consumers — worth remembering that
the file-level manifest was wrong about them.

Also folds in audit finding M5: PlayerClass was a second, server-only
copy of the byte FrameId already replicates. It existed for the meta
shop; with that gone, FrameId is the single frame identity.

Harvest is now single-sink (ledger). HarvestMath keeps its shape so
LANTERN's carried-vs-banked cargo split lands in one place, not two.

295/295 EditMode green, zero compile errors. Subscene re-bake and Play
validation follow in the next commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:59:39 -07:00
kronic 6a412fe3e7 Tooling: security + permission hygiene from the 2026-08-06 audit
- .gitattributes: *.glb / *.gltf now route to LFS. glb is the primary
  Blender->Unity handoff and was committing as raw uncompressible blobs
  (verified: git check-attr returned "unspecified"). Existing files stay
  as plain blobs; new exports go to LFS.
- settings.json deny-list: add git restore / git checkout -- / git stash.
  The list guarded the destructive commands that cannot lose work here
  and omitted the ones that can, in a tree that is routinely dirty.
- settings.local.json: drop the blanket Bash(python *) allow (a full
  escape hatch past the deny-list beside it), drop the serena MCP entry
  (server removed 2026-07-07), and collapse 5 single-use literal command
  allows into two prefix rules.
- workflow-patterns.md: stop telling subagents to prefer serena's
  find_symbol — that template made every spawned agent burn a turn on a
  tool-not-found error. Glob/Grep is now the primary instruction, matching
  memory-protocol.md and CLAUDE.md.

Also done outside git: the leaked ghp_ PAT was stripped from the origin
remote URL, and lfs.allowincompletepush was unset so a failed LFS upload
fails the push loudly instead of reporting success.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:09:40 -07:00
kronic aa5247aff4 Docs: commit basic-memory frontmatter backfill + resolved MCP lock hash
The 13 vault notes were left dirty by basic-memory back-filling
title/type/permalink frontmatter into files written with Write/Edit
(it also strips the trailing newline — restored here so future diffs
stay clean). Content is unchanged; verified frontmatter-only.

packages-lock.json is the com.coplaydev.unity-mcp #main branch ref
silently re-resolving. Pinning that dependency lands with the package
cleanup batch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:09:39 -07:00
kronic 6923888905 Docs: Bevy guide — animation/visual deep-dive (graph add-nodes/masks, readable bone poses, volumetrics) + M1 becomes explicit rig/look proof gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 13:51:37 -07:00
kronic f60e9ab60a Docs: Bevy guide — add BRP/bevy_brp_mcp live-introspection tooling (2026-07 MCP ecosystem sweep)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 12:29:04 -07:00
kronic d0e78e935d Docs: Bevy/Rust/Avian/lightyear greenfield architecture guide for LANTERN (thought-experiment companion to the world-model spike)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 14:40:41 -07:00
kronic 60df50f8fe Docs: silhouette + material-drift follow-ups closed (session log)
Both follow-ups done: Silhouette_Armour (bone-weight broadening, T-pose-safe) + PlayerRigTools drift fix
(reproducible rebuild). Notes the replace_method [MenuItem]-strip gotcha.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:28:13 -07:00
kronic 977b5c1f8e Art: silhouette broadening in-game + PlayerRigTools material-drift fix
Silhouette: broadened the armour's shoulder pads (inflate Shoulder_L/R-weighted pad caps about the
shoulder joint, smooth falloff, arms untouched) + beefed the boots -> Silhouette_Armour.asset (built
idempotently from the stock Synty armour). Bind pose is a T-pose, so broadening is by BONE WEIGHT, not
raw X (a naive |X| threshold would grab the outstretched arms).

Drift fix: baked the correct assignments into the tools so a re-run no longer reverts them -
BuildBathynautPlayer now does the brass/undersuit split + swaps the armour to Silhouette_Armour;
AttachBathynautKit assigns gunmetal tanks / warm beacon / Mark-V brass-gunmetal-glass (3 submeshes).
Full rebuild chain re-run clean + verified live: identical correct diver, reproducible.
(replace_method stripped the [MenuItem] attrs -> re-added; see the swallow-adjacent-line gotcha.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 15:27:29 -07:00
kronic 749aedfbde Docs: Bathynaut helmet Synty-match + in-game body-swap session log + cookbook 14-E
Session log for the 07-24 character work. Adds cookbook 14-E: the per-instance _BaseColor override
gotcha (hit-flash sets URPMaterialPropertyBaseColor=(1,1,1) on player/enemy render children, replacing
the material _BaseColor -> flat null-map materials render cream; color via a solid _BaseColorMap texture).
Corrects the older 'clear the map -> uses _BaseColor' note.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:59:20 -07:00
kronic 9f9c3b07b9 Art: Bathynaut body-swap into the gameplay Player (Mark-V + brass/teal split)
The approved staging character is now in the shipping animated Player.prefab:
- Re-exported the skinned kit FBX dome-less + the Synty Mark-V helmet skinned to the Head bone (3
  submeshes brass/gunmetal/glass); replaced the old fused dome.
- Split materials: brass armour, dark-teal undersuit, gunmetal tank pack, warm shoulder beacon, teal
  porthole glow.
- ★ per-instance _BaseColor gotcha: the hit-flash system sets URPMaterialPropertyBaseColor=(1,1,1) on
  every player render child, which REPLACES the material _BaseColor -> flat-color materials washed
  cream. Fix = drive color via a solid-color _BaseColorMap texture (DiverTex/), not _BaseColor.
Verified live in DevSandbox from the game camera: copper Mark-V + brass armour + teal undersuit +
gunmetal tanks + warm beacon, animated, reads at true game scale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:57:02 -07:00
kronic c78768cbdf Docs: art-dev skill - AI-generate->conform-to-Synty recipe (cookbook 14)
Bank the Mark-V lessons: when scripted primitives can't nail an iconic organic shape, generate with
Hyper3D Rodin then CONFORM - watertight clean-decimate (weld+recalc first, gentle collapse, 0 boundary
edges = hole detector), drop the baked photoreal texture for the shared flat palette, flat-shade,
ontology-tie emissives, and don't skin a meter-scale generated mesh onto a cm-scale rig (~100x shrink).
Added the generate path to the Build phase + a keystone clause (strip a kitbash/gen mesh's own texture).
Also fixed a duplicate section number (12 -> 13).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:28:16 -07:00
kronic be55683f11 Art: restyle Mark-V helmet to match Synty (flat palette + faceted, drop photoreal texture)
The AI-generated helmet's photoreal copper texture + smooth high-poly dome clashed with the flat-shaded
low-poly Synty suit. Restyled to match: dropped the baked texture, decimated 4.2k->1090 tris for chunky
facets, flat-shaded (hard normals), and painted it with the suit's OWN flat palette materials -
M_Diver_Brass bonnet + M_Diver_HelmetMetal accents (valve/collar/bezel) + a new M_Diver_Porthole teal
glass. Now shares the suit's exact materials so cohesion is guaranteed; the teal porthole also ties into
the bioluminescent palette. GLB dropped 1.7MB->736KB.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 14:16:32 -07:00
kronic 69685a665a Art: fix Mark-V helmet decimation holes - clean watertight 4.2k-tri mesh
The 0.10-ratio collapse tore the generated helmet (holes + fragmentation). Re-imported fresh, welded
doubles + recalc normals + deleted loose geometry FIRST, then a gentler 0.18 collapse -> 4198 tris with
0 boundary edges (watertight). Re-exported the static staging GLB; ArtStaging now shows a clean copper
Mark-V (bolted porthole, seam ridges, top valve) at head scale.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:29:03 -07:00
kronic 059458db8b Art: fix Mark-V helmet scale (was 100x too small / invisible) - now a rigid head attachment
The generated helmet is meter-scale but the suit rig is cm-scale (0.01 armature); skinning it applied
the armature's 0.01 -> the helmet imported at ~5mm (invisible). Fix: make the helmet a RIGID
attachment (dropped the mis-bound skinning), sized correctly (~0.5m) at the head. Re-exported static +
ArtStaging now shows the copper Mark-V clearly on the brass diver.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:17:13 -07:00
kronic c88e6f3c7a Art: AI-generated brass Mark-V diving helmet (replaces the golf-ball dome)
Scripted primitive helmets kept reading as golf-ball/blocky-robot, so generated a proper classic
brass Mark-V (Hyper3D text-to-3D): rounded copper bonnet, bolted round porthole, rivet seams, top
air-valve. Decimated 23k->2.3k tris, skinned to the Head bone, replaces the old dome + porthole kit
in the master .blend. Static staging GLB re-exported + ArtStaging updated (reads as a real diving
helmet). Gameplay body-swap (this helmet + brass armour + silhouette onto Player.prefab) is next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 12:08:22 -07:00
kronic 8ddbc70ae7 Docs: helmet reshape + one-model consolidation to the gameplay player (07-22) + AnimatedLitShader atlas-tint gotcha
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 21:19:01 -07:00
kronic 2d47287b15 Art: propagate the Bathynaut overhaul to the gameplay player (kill the lamp-head)
Re-exported the skinned kit from the ONE master .blend with the reshaped diving helmet, grouped into
Metal/Beacon/Porthole SMRs. Darkened the helmet (M_Diver_Metal_Skinned flat gunmetal - the AnimatedLitShader
was multiplying the tint by the gold PaletteAtlas sample, so the atlas map is cleared to force the flat dark
base), kept the warm shoulder-lamp beacon, dimmed the porthole (M_Diver_PortholeGlow_Skinned), shifted the body
to a cool diver tone. Rebuilt Player.prefab via the proven PlayerRigTools graft chain. Verified live in
DevSandbox from the game camera: the gold-dome lamp is gone. Remaining full-match gap (brass-armour/teal-
undersuit split + the shoulder/boot silhouette edits) needs the Synty body replaced by the .blend body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 21:17:50 -07:00
kronic 8df43b8307 Art: Bathynaut helmet reshape (kill golf-ball) + stronger silhouette
Reshaped the helmet dome from a squashed sphere into a diving-helmet form (modest vertical stretch +
flatter faceplate + chunky collar ring + bigger porthole bezel); broadened the outer shoulder pads and
beefed the boots for a heavier, more imposing silhouette. Master .blend is the single source; static
posed GLB re-exported + ArtStaging updated (connected instance, diver materials intact).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:58:15 -07:00
kronic ac8ddd0f36 Docs: ArtStaging overhaul (07-22) - game-cam POV system + Bathynaut lamp-fix in session log
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:35:36 -07:00
kronic 8b50753f93 Art: ArtStaging overhaul - game-cam POV system + Bathynaut character (no longer reads as a lamp)
Camera: runtime POV switcher (StagingCameraSwitcher; V/]/[ cycle, 1-4 jump) with POV_1_Game matching
the in-game rig (Pitch45/Dist13/FOV55) as the default + hero/front/close-up; deep-water SolidColor
clear kills the no-skybox corner bleed at the wide game framing.

Character (the 'reads like a lamp' fix, operator-directed): darkened the helmet to dark gunmetal with
a dim lit porthole and moved the warm beacon to the shoulder lamp (light is carried, not the head);
color-blocked the body into a brass-and-teal deep-sea-diver palette (brass armour / dark-teal
undersuit / dark-metal kit) for value contrast + identity at game scale; bulked the back tank-rig
silhouette. Master .blend keeps rig+pose+materials re-editable; posed static GLB reimported into the
connected prefab instance with instance material overrides reset to the GLB's diver materials.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:34:36 -07:00
kronic 5e0044b492 Docs: suit pose shipped (07-22) — session log + roadmap A0 update; Blender pose-bake gotchas
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:44:58 -07:00
kronic eaff8d16e3 Art: pose the Bathynaut suit out of T-pose (rig cleanup + posed static bake)
The suit .blend was rigged, but the base body/armour/head had vertex groups with NO Armature
modifier (why the export baked a static T-pose) and 2 orphan lamp parts were unweighted. Added the
modifiers, weighted the lamp parts to Spine_03, posed a grounded combat-ready stance (shoulders
down ~63deg, elbows braced forward ~22deg), baked the pose into static geometry (depsgraph
new_from_object) and re-exported. Master .blend keeps the rig+pose re-editable; the in-place GLB
overwrite flowed into the connected ArtStaging prefab instance (38 parts intact, materials +
extracted staging emissive preserved). Suit width 2.05m T-pose -> 1.04m posed; A0 silhouette now
reads from the top-down game camera.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:43:36 -07:00
kronic f3aec94c48 HUD: ability bar defaults hidden until a local player exists
Fixes the empty-bar leak into ArtStaging / menu / pre-connect: AbilityBarSystem.OnUpdate
early-returns when there is no valid NetworkTime, so the 'hide when no local player' line never
ran. Root now DisplayStyle.None at build, flips to Flex only when the local player is found —
also kills any pre-connect flash in gameplay.
2026-07-22 18:26:24 -07:00
kronic 1015f5d3fa Art: ArtStaging A0 value/composition rework (light-is-territory)
Global fill cut (KeyWarm 1.25->0.15, CausticsSpot 32->9, GloamFill 0.5->0.22) so the scene
goes dark-except-where-ours-reaches; WarmPool point light -> downward warm SPOT = a defined
ground-territory disc that lights the suit top-down (a low point light was blasting the chest
into a blown bloom streak). Suit lamp + Drowner gloam emissives extracted from .glb sub-assets
to durable .mat assets (M_Suit_LampWarm_Staging, M_Drowner_GloamAccent_Staging). Recomposed the
triad (crate in-pool / Drowner faced-to-camera at the lit edge / suit center). Grayscale-verified:
the beacon dome is the value winner + the warm disc reads as territory. Suit pose still T-pose
(a Blender rig sitting; A3-adjacent).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 18:26:24 -07:00
kronic e03be4861f Docs: 07-21 ArtStaging A0 value/composition rework log (J) + roadmap A0 update
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 20:46:26 -07:00
kronic 5569fcd552 Fix: socket auto-recast at every cooldown reopen (raw InputBufferData IsSet = ever-pressed)
Netcode accumulates InputEvent counts on the wire; only the decoded component is delta-corrected.
AbilityFireSystem's windup resolve gated on raw IsSet, so after the first press every socket
re-fired the instant its cooldown reopened (live repro: recasts at exactly t3933/t4353 with zero
input). Gate is now a count-STEP vs the previous tick's command; missing prior command = no-fire.
Also lands the G6 ConeContactPending schedule machinery + the cone test fixture (wire-true
monotonic counts) + regression Cast_Never_Refires_When_The_Cooldown_Reopens. 430/430; live proof:
one tap -> one cast -> silence across 2+ reopens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:28:08 -07:00
kronic 040463ad07 HUD: ability/cooldown bar (sockets 1-4 + dash); socket-0 charge strip removed
AbilityBarSystem (own UIDocument, sortingOrder 49, the B5 sibling pattern): per-slot Spark
initials/name from the AbilityDatabase blob, archetype tint, drain overlay + seconds countdown +
ready flash; dash rides DashCooldown vs TuningConfig.DashCooldownTicks (Defaults() fallback).
HudSystem's single-socket blue charge strip removed (it tracked socket 0 only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:28:08 -07:00
kronic 2aebc37115 G6+G4: Cone damage-at-contact + zone fill telegraph (ZoneEffect GhostFields) + co-op saturation budget
Cone/SpecialSlam damage lands at its visual contact via ConeContactPending (knob 32, 0=legacy;
early-flush + resolve re-validate; death + BOTH class-swap paths drop armed pendings — fixes the
shipped melee death-strand in the same stroke). Zones: [GhostField] Caster/Radius/NextTick +
ZoneTelegraphSystem (Geyser latch contract on InterpolationTick; rim = true radius; arm grows,
persistent phase drains). Cone cues latch to contact (FireStartRaw, C14); TuningConfig.Defaults()
fallback at client cue sites (release-build timing fix). G4: SaturationMath ally-FX degrade
(living-enemy census, solo-exempt; enemy telegraphs structurally exempt) + CombatStressDebug +
overlay saturation rows. Reviews wf_98bf1268 (13 confirmed folded) / wf_9757d214 (5 confirmed fixed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:27:50 -07:00
kronic 9d6977cd64 Docs: 07-21 auto-recast root-cause correction (wire-count InputEvent ★), harness lessons, CLAUDE.md net-zero trims
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:19:16 -07:00