Commit Graph

12 Commits

Author SHA1 Message Date
type-two
975e1a4ec3 [lane B+C] The combo meter starts paying, allies stop counting as kills, and par stops
meaning "miss nothing"

Audit item 13 — the systems the levels were authored against, none of which worked.

THE COMBO MULTIPLIER DID NOT MULTIPLY. balance.js's own comment cites the GDD line "score x style
multiplier"; what shipped was a 2.5 s window and a counter. The HUD printed "x7 chain",
comms shouted about it at 5, the music added a layer at 3 — and combat/index.js added the flat
base score every time. Every par.score in every level was authored against a lever that did not
exist, which is exactly why they all sat at 75-94% of the un-multiplied ceiling. Now step 0.12,
cap 2.5 at a 14-chain: reachable in L4's ileum and nowhere else, because a cap you hit every
fight is not a skill expression. Verified in the running game: six chained floaters pay
100/112/124/136/148/160 where they used to pay 600 flat.

ALLIES COUNTED AS KILLS. combat/index.js did an unconditional score.kills++ on enemy:die, and
the ally branch of enemies.js emits enemy:die with score 0 — so shooting a lactobacillus reef,
the one act this game's whole reputation loop exists to punish, ticked your PATHOGENS tally up
by four. The pause card and the operative record told you that you were doing well at the exact
moment the biome decided otherwise. enemy:die carries `foe` now. Verified: seven deaths, six
kills.

THE PAR CHECK WAS COUNTING A THIRD OF THE GAME. scoreBudget() summed spawn events and nothing
else — not the 500 points a biopsy sample is worth, not the several thousand a boss puts in the
room. Four of six levels therefore shipped a permanent "par.score exceeds the kill budget"
warning, and a warning that is wrong two thirds of the time is one everybody learns to scroll
past. It counts pickups and boss rings now (nodes once per phase — a floor, since a phase
re-rings for as long as the fight lasts and no honest ceiling exists). Six levels, zero
warnings, and the ratios that were 94/75/52/59/44/32% of a fictional budget are now real.

AND THE CHECK THAT SHOULD HAVE CAUGHT L1. The only par test asked whether par exceeded the
ceiling. L1 sat at 94% of it: par the tutorial and you had to kill all eight foes, take all
seven orbs and all three samples — miss ONE sample and the medal was gone. That is a
clear-sheet requirement wearing par's clothes, in the level that exists to teach. par.score is
1900 now (71%), and the validator warns above 85% so the next one gets caught by a machine.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 22:19:34 +10:00
type-two
a3784c0371 [lane B+C] L5 Large Intestine: the dark, the gas, and The Blockage — the campaign is complete
The last level. 6 segments, 3200 units, 51 events, and the end of the game: `next: null`.

THE GAS. GDD: "flammable gas pockets: shooting inside one detonates it (hurts everyone —
risk/reward)". Built literally, as an entity whose kill() throws a blast:
  - one cannon pellet sets it off (hp 10) — the hazard has to be hair-trigger or "don't shoot
    near it" is not a constraint;
  - the blast reaches 16 units, FALLS OFF with distance, wipes foes, hurts YOU, and chains
    through other pockets, so a corridor of them is a fuse;
  - foe:false, so the pocket itself scores nothing — you are paid for what the blast KILLS,
    which makes the reward self-balancing.
Measured: one pellet into a line of four detonated all four; a blast took 4 virions and paid
240; the player takes 30.3 coat point-blank, 15.4 at mid range and 0 at 20 units. "Detonate
from max range" — which C's Blockage spec calls optimal play — is a real, measurable skill.

And the cruelty that makes the level: fog 0.075 is the densest in the game and there are no
real-time lights in GUTS, so THE POCKETS ARE THE LIGHTING. Clearing a room safely also clears
the lights. No new rendering path was added for the dark; the biome's fog and the emissives
already did it.

THE BLOCKAGE (LANE_C_NOTES §Boss specs 3) — 5 armoured plates -> 4 tumbling chunks -> the core
under a 30s doom timer, with gas pockets seeded into every phase, so the boss's own room is its
worst weakness. First cut ran 25.7s, shorter than the mid-game Guardian, which is the wrong
shape for a finale — its windows are continuous by spec, so hp went 120/130/150 -> 220/230/280.
Now: 49.7s brute-forcing the plates, 30.2s using the gas. The room is the weapon, and the
difference is 40%.

The reputation loop lands here as designed: two biofilm gates, the second sixty units from the
final boss. Five levels of deciding whether to shoot the cyan resolve into three phage escorts
and a full coat, or nothing at all, in the dark, at the door.

Registering L5 also closes two dangling threads — L4's ileocecal valve and LS_appendix's exit
both pointed at it and now resolve.

C's tooling: 6/6 levels valid, sim par 540 vs par-pace 505 (reachable, with room for the boss),
pressure 1.33/4.09/3.21/5.75/4.12, min clearance 8.30 vs the 2.5 law. par.score corrected
7000 -> 4000 and a missing checkpoint added at 3060 (the 30s law caught a 34.8s gap I had left
between the boss and the gate).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 11:35:28 +10:00
type-two
f9aeaf259f [lane B+C] L1 Oral Cavity: the tutorial, and the Mast Cell — the boss you win by not shooting
L1 has been a 2-event skeleton since round 1, waiting (in its own status field) on "Lane B's
arena-mode controller". That exists now, so C's round2_plan is built as written: molar
crush-cycles, the saliva tide, amylase droplets, the three samples exactly where C put them
(behind the molars / under the tongue / a tonsillar crypt), and the epiglottis exam. 24 events.

The teaching order is the level: move (orbs, no current) -> LOOK (the arena opens and you can
stop, which no other level allows) -> shoot (one droplet, then two) -> coat (the molar ring
grazes you, mucin is right there) -> RESTRAINT -> flow returns (the tide, the funnel) -> the
exam. Every verb alone, in the calmest room in the game, before anything is asked of it. Two
hazards are deliberately re-used rather than invented: the molar crush IS a ring_gate and the
saliva tide IS a non-lethal surge, so L2's gates and its reflux finale become callbacks instead
of new ideas.

THE MAST CELL (LANE_C_NOTES §Boss specs 5) sits at the exact midpoint. A sentinel immune cell:
cyan, no attack, no chase, inert aura (auraDps 0 — it shares the floater's drift branch and its
aura does literally nothing). Fly past and the encounter just ends. Shoot it and it degranulates
— droplets close in from the walls (which is how "the cavity swells shut" is expressed, because
a runtime tube radius is not in Lane A's contract), the coat bleeds, and past half health the
airway closes on a 25s timer. You always get through. The only question is what you get through.

It needed a new boss `kind: 'sentinel'` with its own update path, because forcing a thing with
no rings and no windows through the phase machine would have been worse than a branch. Damage is
the failure state, so every number describes a consequence rather than a fight.

Verified, all three player choices: SPARE -> "spared", coat untouched at 100, karma +25, score 0.
SHOOT -> "degranulated", coat 100->56. KILL -> "anaphylaxis-survived", coat 100->6. Score is 0 in
every case: shooting it buys literally nothing, so no medal can ever reward the mistake.

Two bugs the testing paid for: `def.phases[0]` crashed on a boss that has no phases, and the
first degranulation drain (5/s) was UNDER the player's 8/s coat regen — measured coat going
80 -> 100 during a histamine cascade, i.e. the punishment was healing you. Now 14 and 22, which
are net -6/s and -14/s against regen.

L1 is no longer a skeleton: its checkpoint-gap and zero-samples warnings are gone, and the
campaign now opens on a real tutorial instead of an empty mouth with a gate at the end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 00:13:52 +10:00
type-two
7709484ea7 [lane B] The Pyloric Guardian: the first boss, built to C's spec
GUTS has had three bosses designed and none implemented. This is the first, and the framework
the other two should reuse. Built to LANE_C_NOTES §Boss specs, phase table and all.

WHY IT IS SMALL. A boss here is not a parallel entity system, it is a SCHEDULE over the enemy
pool. The nodes and vents are ordinary enemies, so weapons hit them, the InstancedMesh draws
them and enemy:die reports them for free; the fight's identity lives entirely in WHEN they can
be hurt, which is two flags (`invuln`, `armor`) honoured by enemies.hurt(). boss.js is a state
machine and nothing else.

Faithful to C's table:
  P1  4 rim nodes, iris opens 2.5s every 8s
  P2  4 nodes, opens 1.8s every 5s, TWO armoured — and only the antacid torpedo strips them
  P3  the core alone, 60hp = C's "x5 hits", plus the 25s doom timer that kills you if you stall
Telegraphs are C's too: a "deep gulp" 2s before each window (boss:telegraph + audio), and nodes
go amber -> white-hot exactly when they become shootable — one uniform driven off `invuln`, so
the thing that glows IS the thing you can hurt, always.

Vents are turrets at fixed arcs: a turret already fires a telegraphed homing projectile on a
cooldown, which is what an acid vent is.

hp is B's call (C: "numbers need B's framework to tune against"). 150 per rim node means a ring
outlives its first window, so you must come back for it — which is what lets the stomach's
coat-drain be the real clock. C's pillar, verified: parked in the lair for 20s without fighting
back, coat goes 100 -> 0. "A race against your coat, not its hp."

Verified end-to-end: a paced pilot (11 shots/s, ~70% uptime) wins in 38.8s across 9 windows at
0.38 open-duty (spec: 31-36%); phases report 4/0 armoured, 4/2 armoured, 1 core; damage is
refused while the iris is shut; the two armoured nodes consumed exactly 2 torpedo strips.

Not included: C's arena churn (a force on the player, the one part of the spec that is a feel
change rather than a schedule) — noted for the round that tunes it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 22:50:56 +10:00
type-two
fce26abfab [lane B+C] Three more microbes: the swarm, the driller, and the reward beacon
Roster round-2, batch 2 — three new archetypes, mostly reusing existing behaviour branches:
- virion (norovirus): a tiny fast swarmer, its own pool of 40 so a cloud reads as a swarm.
  Shares the seeker branch (chase + dissolve on contact). The arithmetic threat. -> L5.
- driller (H. pylori): a TANKY corkscrewer (hp 50). Seeker chase + a rotating perpendicular
  drift (the helix), and it does NOT dissolve on contact — it grinds you and keeps drilling,
  so you must out-damage it. A new behaviour branch. -> L3 stomach; previewed in L2 at s2240.
- beacon (Faecalibacterium): the reward-state commensal. Shares the commensal branch, heals
  AND gives the biggest standing (25) with no coat cost. A bright cyan octahedral gem, the
  special one of the family. -> L5; previewed in the L2 breather at s1580.

Verified in-engine: driller corkscrews + survives contact; virion is tiny + dissolves; beacon
heals coat and spikes standing to 50% in two tends. Three clean distinct silhouettes (gem /
cone / dot-swarm).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 00:03:34 +10:00
type-two
b2d9b46c30 [lane B+C] Giardia: the latch, and boost becomes a dodge
The first foe with an ATTACH state machine, and the first that gives boost a defensive job.
Giardia chases like a seeker, CLAMPS its ventral suction disc onto your hull, and drains coat
while it rides — until a boost's i-frames throw it (or it lets go after attachMax, then can't
re-grab for reattachCd). The counterplay is timing a boost, not out-running it.

- enemies.js: `latcher` archetype — a two-state (chase / attached) branch. Attached, it rides
  player.pos + a spin offset and drains `drain`/tick; ps.iframeT > 0 (boost) detaches and flings
  it clear. Amber tetrahedron (the trophozoite), so a clinging one reads on the hull.
- levels/enemies.js: ARCHETYPES += latcher; catalogue `giardia_latch` (biome small_intestine).
- L2: one Giardia previewed on the open thoracic run (s1940) — it belongs to L4's villi maze,
  but the grab works in a flow tube, so it teaches the boost-dodge verb now.

Verified: it chased + clamped, drained coat 100->84 while attached, and a simulated boost
(iframeT) shook it off.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 20:45:21 +10:00
type-two
b0abbaf76b [lane B+C] C. difficile: the kill that isn't clean
A foe whose corpse keeps hurting — the definitive colon enemy. C. diff drifts with a corrosive
aura like any floater, but on death it leaves a SPORE_CLOUD: a static acid-green zone that
damages anything in it for ~5s, then disperses. Killing it costs you the ground it died on
(shoot the cloud to clear it, or fly around).

- enemies.js: two archetypes. `cdiff` (amber octahedron, shares the floater drift+aura branch)
  drops a `spore_cloud` in kill(), placed at the death spot. `spore_cloud` (acid-green per the
  readability law, foe:false so the phage ignores it and clearing it never scores) is static,
  damages on contact, and counts down a `life`. Entities gained a `life` field (only the cloud
  uses it).
- levels/enemies.js: ARCHETYPES += cdiff; catalogue `cdiff_bloom` (biome large_intestine —
  registered for L5's encounter pass, not shoehorned into the esophagus).

Verified: killing a cdiff dropped a cloud at its death spot; sitting in it dropped coat 100->89;
the cloud expired after its life. Amber octahedron vs acid-green blob — clean read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 20:38:15 +10:00
type-two
aae1ebdcfd [lane B+C] Akkermansia: spend coat to summon allies sooner
The cleverest node in the reputation loop — a commensal that eats your mucus. Brushing it
COSTS coat but fills BIOME STANDING fast (gain 12 vs flora's 5): the "spend coat for a quicker
phage ally" trade against flora's safe heal-and-slow.

- enemies.js: flora's branch generalised to a shared COMMENSAL branch, sign-flipped by data
  (cfg.coat gifts, cfg.coatCost nibbles); flora:tended now carries `gain`. New `mucus_grazer`
  archetype — cyan (still flora, readability law), but a dodecahedron amoeba-blob so the coat
  cost reads as a different bug, not a betrayal by the healing rod.
- combat/index.js: standing gain comes from the event's `gain`.
- levels: ARCHETYPES += mucus_grazer; catalogue `akkermansia`; L2 teaches it at s210, right
  after the flora reef so the same-colour / opposite-trade contrast lands.

Verified: brushing it dropped coat 100->88 while standing hit 36% in 3 tends (4x flora's rate);
distinct blob vs the flora capsule.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 20:30:17 +10:00
type-two
71b0663b77 [lane B+C] The phage ally: tend the flora and the biome fights for you
The first ALLY, and the reputation loop that earns it — "don't shoot the biome, it helps you"
(V2_IDEAS) made mechanical.

- combat/enemies.js + balance.js: the `phage` archetype. A friendly seeker that hunts the
  nearest FOE (not the player) and rams it — a bacteriophage bursts its host after `bursts`
  hits. Fast (26 > every foe) so it catches things, fragile (hp 8) so it is spent, not a wall.
  Entities now carry a `foe` flag (foes true; flora + phage false); kill() generalised so ANY
  ally scores nothing and never combos. Fallback silhouette is a bare icosahedron = the phage's
  real icosahedral capsid. Teal (EMISSIVE.ally), distinct from cyan "don't shoot" flora.

- combat/index.js: BIOME STANDING — one run-scoped float. flora:tended raises it, flora:harmed
  drops it 25; at the symbiotic threshold the biome BUDS a phage near you (capped 3, 3s cooldown),
  spending standing. Emits `standing {value}` for a future Lane E meter.

- levels/enemies.js: ARCHETYPES += phage; catalogue `phage`.

Verified deterministically in-engine: tending flora budded a phage, which hunted a spore_pod foe
(30hp) and rammed it dead; three-colour readability holds (amber foe / teal ally / cyan friend).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 16:36:03 +10:00
type-two
0ac11ae040 [lane B+C+D] Microbes, friend and foe: the GLB loader, a friendly commensal, and a distinct-mesh yeast
The round-2 microbe pass, vertical slice. Cross-lane (integrator call): the point was one
thread proven end to end — art pipeline in, friend and foe out.

- core/assets.js (D): the GLB loader, finally wired. GLTFLoader was vendored but never
  imported and the manifest had 0 models, so enemies.js's `glb?.geometry` hook was dead code.
  Now every manifest model preloads (AWAITED — pools set geometry at construction, a late load
  can't retrofit an InstancedMesh) into ONE BufferGeometry normalized to a unit bounding
  sphere; get('models',n).geometry lights up the hook. Optional-asset law intact: any failure
  -> no geometry -> procedural fallback.

- combat/enemies.js + balance.js (B): two new archetypes.
    flora     — the FIRST FRIEND. Cyan (ART_BIBLE: flora reads friendly). Drifts like a
                floater but its aura HEALS (sheds coat), never harms. Shootable — and shooting
                it is the reputation trap: kill() gives no score, no combo, emits flora:harmed.
                Verified: the aura fires player.refill({coat}); a shot flora scores 0.
    spore_pod — a foe yeast cluster: the floater's drift+aura, but its OWN pool so its blobby
                GLB will not reskin the food-debris floater.
  Pool geometry now scales a GLB by the archetype radius (GLBs ship unit-normalized).

- levels/enemies.js (C): ARCHETYPES += flora, spore_pod; catalogue entries lacto_drifter
  (Lactobacillus) + yeast_pod (Candida). L2 gets a light teach beat in the calm opening — a
  friendly reef at s180, one yeast at s300, both off the racing line.

Ships playable as glowing primitives (a capsule + an icosahedron), exactly like the rest of
the round-1 roster; the Trellis/Hunyuan meshes drop in via the manifest with no code change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:13:30 +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