Commit Graph

7 Commits

Author SHA1 Message Date
type-two
426bf3f33d [lane B] Hits land on the thing you hit, and the reticle finally says so
Audit item 12.

THE WHOLE COHORT WAS BLINKING. Enemy flash was ONE uFlash uniform per type, and the comment
above it said so and deferred the fix: shoot one bolus chunk in a room of eight and all eight
blinked white. That is not weak feedback, it is WRONG feedback — the single signal that tells
you which body absorbed your shot was being broadcast to every body that did not, in a fogged
tube where telling them apart is the entire skill. Per-instance now: an `aFlash`
InstancedBufferAttribute written at the same compacted draw index as the matrix (they must
match, or the flash lands on somebody else's body once anything in the pool dies). Verified
with five floaters drawn and the middle one shot: aFlash reads [0, 0, 0.9, 0, 0].

emissive.js declares the attribute unconditionally, so a plain Mesh or a pool that never
attaches one gets WebGL's default 0 and renders exactly as before. uFlash stays for the one
effect that IS per-cohort — the boss's open-node tell, where every node opens at once.

THE RETICLE ONLY KNEW ABOUT THE TRIGGER. setHot was driven by `intent.fire`, so the instrument
in the middle of the screen reported whether the player's own finger was down and said nothing
about whether the shot connected. In a game where a foe can be hit-and-unharmed (armoured
plate), hit-and-shielded (the Guardian's shut iris) or simply missed in the fog, that is the
most useful thing it could have been telling you — and the shut-iris case is exactly the one
where a player without feedback concludes the gun is broken. enemies.js now announces
`enemy:hit {dmg, blocked, dead}` and the reticle pops for it: 0.35 blocked, 0.7 landed, 1.0
kill. The tell is a SIZE pop, not brightness alone, because firing already brightens it and one
signal cannot do two jobs — firing is dimmed to 0.55 to make the room.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:25:50 +10:00
type-two
b9d9d816ad [lane B+E] Stop the game lying: the death sting at living players, the stuck hiss, and a crew naming the wrong organ
Four audio/comms cues wired to the wrong condition. All four are written content that has been
in the repo for rounds, firing at the wrong moment or never firing at all.

THE DEATH STING PLAYED AT LIVING PLAYERS. finish() mapped every non-win outcome to `death` — and
`death` is not a stinger, it is a ~12 s noise table (synth.js). So surviving L1's Mast Cell
("degranulated"), outrunning the Tapeworm ("escaped") and clearing the Sovereign's Toll all told
an alive player they had died, over the top of the next twelve seconds of play. Outcomes are
classified now: won/spared/communion triumph, alive-but-it-went-badly gets a downbeat, actually
dead gets the sting, and housekeeping (reset/dispose) is silent because nothing happened to the
player at all. Verified: surviving the Mast Cell no longer emits `death`; dying still does.

THE PHASE TELL WAS A SUSTAINING HISS. boss.js used `overheat` as its phase-change cue, but that
layer sustains until the engine sends `overheat_clear` on a combat:state.overheated falling edge
— which a boss never produces. Every phase change leaked a ~12 s steam hiss and ducked the mix
under it for the rest of the fight. Three sites, now short cues.

THE CREW HAS NEVER MENTIONED HULL DAMAGE. comms.js gates its two hull lines on
`kind === 'hull_hit'`, but `kind` is the damage SOURCE ('dart'|'acid'|'gas'…), so that branch has
never once been true in the game's life and every hit read as a coat graze. damage() already
computes `leak > 0` for its own audio cue — the bus event now carries it as `hull`.

AND IT NAMED THE WRONG ORGAN. There was exactly ONE boss line — "that is the pylorus. it does not
open for you." — fired for all five bosses, so the tutorial's Mast Cell and the campaign's final
boss were both announced as a stomach valve. Per-boss lines now, off boss:start (which is the only
place the id is known, and for the Sovereign whether there is a fight at all). The Mast Cell's is
doing real work: "wait — wait. that is a mast cell. it is not hunting you." / "do NOT shoot it."
The crew is the game's only tutorialisation channel, and that is the lesson L1 exists to teach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 19:45:29 +10:00
type-two
87a4803bde [lane A+B+C] L3 Stomach: the acid sea, and pH as a resource you can see
The last skeleton. L3 has been 4 events since round 1, waiting (in its own status field) on
"the pH field" and a boss framework; both exist now, so the level is authored: 46 events,
2500 units, the sea, and the Guardian at the end of it.

THE PH MECHANIC, finally load-bearing. Lane A built an acid plane in round 2 and left two
hooks on it — depthAt() labelled "B:" and set() labelled "C's event pump drives this" — and
nothing had ever called either. Now:
  - player.js scales the biome's ambient coat drain by SUBMERSION. Stomach 2.2/s x5 = 11/s
    under the surface (measured: 8/s net of regen, and regen is off for 2.5s after any hit,
    so it is brutal mid-fight); x0.15 above it, which is effectively safe air.
  - a new `acid` level event names a target height and boot eases the sea toward it in step(),
    so a RISING TIDE is a beat C can author. L3 uses two: -14 -> -6 mid-sea, then -6 -> +2 in
    the antrum, where the surface goes ABOVE the centreline and the safe lane simply stops
    existing. Measured: the identical spot (y -5) goes from 0/s to 9.67/s across that tide.
This is the GDD's "mucus shallows are safe lanes" expressed as DEPTH rather than wall-hugging,
which is better on two counts: it reads instantly (you can SEE the surface) and it makes the
vertical axis matter in the one level that is a room. Every collectible worth having is under it.

Gated on the sea's s-span, not on depthAt alone — depthAt returns a bare -1 outside the span
and that is indistinguishable from "1 unit above the surface", so reading it naively would have
quietly scaled ambient drain on every level in the game. Caught before it shipped.

Level: cardia (still teal, so the fundus can REVEAL the sea) -> fundus (the teaching room:
huge, low tide, nothing kills you fast) -> body (the sea proper, tide rises while you are in
it) -> antrum (no safe air left, only speed) -> pyloric antrum (the Guardian). H. pylori is
taught alone in the dome and tested in a rising sea; pepsin swarms scale 3 -> 6; bezoar_chunk
is a new catalogue row for the stomach's traffic.

L3 has NO commensals and no biofilm gate, which is anatomically exact — acid sterilises, which
is why H. pylori is famous for surviving it. So BIOME STANDING goes quiet for nine minutes and
comes back in L4, which makes L4's colonies read as relief rather than furniture.

C's sim: par 540s vs par-pace 555s ("requires pushing above par-pace"), pressure
3.75/1.50/2.44/2.16, min clearance 7.15 vs the 2.5 law, all 21 checkpoint gaps under 30s.
par.score corrected 9000 -> 5500, because 9000 was above everything the level can actually pay.

Deferred, and said out loud rather than quietly dropped: churn cyclones. A vortex that flings
the player is a FORCE and no hazard in the tree applies one; it wants the same A/B work as the
Guardian's arena churn and should land with it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 00:26:31 +10:00
type-two
4ee6ae49a1 [lane B+E+F] Arena mode: in a room, you own the forward axis
The 6DOF unlock the player header has promised since round 1 ("Arena/6DOF mode is round 2;
modeAt() is guarded below"). It unblocks L1 and L3, which are both arena levels in the GDD and
have been skeletons waiting on this.

The insight that made it small: spline space ALREADY spans a room — s is the axial coordinate,
x/y are the cross-section — and world.collide() already resolves against arena spheres
(arenaSpatial, Lane A round 2). So this is not a new controller. The only real difference
between a tube and a room is WHO OWNS s: in a tube the flow carries you and you may only trim
it +/-40%; in a room `intent.throttle` becomes signed thrust with its own damping, so you can
stop dead and you can reverse. That is the whole feature, and it is what "slower, spatial,
exploratory" actually means in play.

- tuning.js: arena{thrust 44, maxSpeed 11, damping 2.2}. maxSpeed sits UNDER the esophagus's
  12-20 flow on purpose — the GDD asks for slower.
- player.js: st.vs (axial velocity), the mode branch, surf disabled in rooms (no travelling
  wave to ride, and a hanging surfBlend would speed-lock you to a crest that isn't there),
  respawn() clears vs so a death in a room can't leak momentum into a tube.
- player:state gains `mode`; `flow` reads 0 in a room and `speed` goes signed.
- hud.js prints "6dof · free" instead of "flow 0 · thr 100%" — two lies on one instrument.

Safe by construction: both arena triggers are opt-in (level.arenas, or a segment marked
mode:"open"). L1/L2/L4 declare neither and are byte-for-byte unaffected.

Verified in L3's stomach: tube hands-off carries you at 4.53 u/s, acid-sea hands-off coasts to
a dead stop (0.0), and a room lets you travel BACKWARDS (-3.8u). Bus reports
{mode:arena,flow:0,speed:0} vs {mode:tube,flow:3,speed:3}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 22:39:06 +10:00
jing
29cca53675 [lane F] Mid-round-2 recovery: the glue that makes L2 a game
All round-2 lane sessions were cut off 15:08-15:16 before NOTES/commits; the three prior
commits preserve their work verbatim. This commit is the wiring B was cut off inside of,
plus the run-state boot owes per ruling #4:

- combat/index.js: fiction-id -> archetype resolve via C's getEnemy() (the round's declared
  ONE blocker), theta-array + spread fan-out for group spawns, hazards + pickups constructed
  and updated (order: enemies -> weapons -> hazards -> pickups, so a torpedo can neutralize
  a surge the frame it detonates), pickup score/samples onto the combat:state scoreboard,
  combat.reset(fromS) for respawn.
- flight/player.js: kill/shove/refill — the API B's new modules call; kill ignores iframes
  by design (boost-dodging through C's lethal acid wall is not a read). Deleted the
  crestSpeed fallback guard per its own comment — A's law landed.
- boot.js: run state. checkpoint crossed -> remembered; death -> 2s -> respawn at it +
  combat.reset() re-arms hazard timelines (pump deliberately does NOT rewind: no
  double-spawns, collected stays collected); gate -> level:complete {stats, par} for E's
  medal card. Exported step(dt) so the stepped-sim harness drives the REAL loop.
- ROUND2_INSTRUCTIONS: MID-ROUND STATUS box for the resuming lanes; f-progress entry.

Verified (deterministic stepped sim via boot.step, :8140): full L2 run fires all 47 events
with ZERO console warns (was: 11 spawn no-ops), all 5 hazard telegraphs, 2400 proximity
ticks, 14 pickups collected, finish 157s vs par 180 while surfing 94s of it. Death path:
throttle 0.7 into the finale -> killed by acid s=3386 -> respawn at Cardia Approach 2980 ->
surge re-arms -> second attempt escapes -> level:complete {deaths: 1}. Survivable AND
losable. Evidence: docs/shots/laneF/round2_L2_integrated_full_run.png. qa.sh GREEN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:39:44 +10:00
jing
4ed3433e55 [lane B] Round 2 WIP (session cut off): surf speed-lock, hazards + pickups modules, rho-fraction spawns
Landed before the cut: surf as a speed-lock onto world.crestSpeed(s) with surf.authority
(ruling #1) — riding beats throttle and holding a crest is throttle discipline; tuning.js
header corrected to the real wallRho frame (C's flag); enemies.js reads rho as a FRACTION
of safe radius (C's placement law); balance.js hazard + pickup constants; NEW hazards.js
(reflux_surge / aortic_squeeze / ring_gate, self-scheduling telegraphs, reset(fromS) built
for respawn) and NEW pickups.js (all five kinds, shape-coded).

Cut off before: wiring — nothing imports hazards/pickups yet; the fiction-id -> archetype
resolve (round-2 task #1) does NOT exist despite the enemies.js comment saying index.js
does it; player.kill/shove/refill are called but were never added; checkpoint/respawn and
gate -> level:complete not started; NOTES unwritten. F lands the glue next commit.
Committed by F to protect the shared tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 16:26:15 +10:00
jing
0a038582a7 [lane B] Round 1: flight controller, cannon, enemy framework on the stub
Flight (js/flight/): tube-mode controller in spline space (s,x,y) — flow-locked
forward motion, throttle as a spring-back lean, boost + i-frames, banking chase
cam on the parallel-transport frame, arcade wall response (shove + graze damage,
never a hard stop). Twin-stick input: WASD/left-stick moves the ship in the disc,
mouse/right-stick aims, Shift/Ctrl is throttle. Gamepad supported.

Combat (js/combat/): lysozyme cannon (pooled, instanced, heat-limited with
hysteresis lockout) + antacid torpedo emitting level:neutralize. Enemy framework
with floater/seeker/turret and turn-rate-limited homing darts, spawned from C's
level:event. Coat/hull model and the full bus surface E builds against.

Measured (deterministic stepper): flow-lock exact (3s at flow 14 -> s=44.0);
boost peak 30.8 u/s; heat 4s fire -> 2s lockout; wall slam 13.3 coat at 11.3 u/s
with forward speed untouched; 10 combat draws at 55 live enemies (budget <=80);
0.10 ms/frame CPU; 0 leaks over 20 create/dispose cycles. fps NOT claimed — rAF
does not run in the automated pane. Evidence: docs/shots/laneB/.

Three findings escalated in LANE_B_NOTES:
- qa.sh gate #1 is a no-op: node --check silently exits 0 on ESM, so the syntax
  gate has never checked anything. I shipped a real SyntaxError past a GREEN qa.
  Fix + verification handed to F.
- Surf is structurally broken: the peristalsis wave (13.64 u/s) is slower than
  the player (19.6 u/s), so level 2's signature mechanic loses to mashing
  throttle. Not tunable from this lane; escalated to A/F with options.
- Custom ShaderMaterials need #include <colorspace_fragment>, else ART_BIBLE's
  hostile amber reaches the display as rgb(255,26,6). Fixed here; stub/walls
  inherit it.

js/flight/dev.html is a dev harness (boot.js has no player wiring yet); it
retires once F pastes the snippet in LANE_B_NOTES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:56:32 +10:00