Commit Graph

10 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
5a891107e2 [lane B+F] The enemies were never wearing their meshes: boot forgot to pass assets to combat
Chasing "more detailed looking enemies" turned up the reason detail never landed, and it was
not the art.

boot.js built combat as `createCombat({ scene, world, bus, rng, flags, player })` — no `assets`.
createCombat defaults it to null, so createEnemies got null, so `assets?.get?.('models', type)`
returned null for EVERY archetype and every enemy in the game has been quietly drawing its
procedural primitive. Lane D's GLBs loaded correctly, verified correctly, and were never once
rendered. spore_pod has been an icosahedron since the day it shipped; the Giardia and H. pylori
meshes from an hour ago were cones.

This is the nastiest shape a bug can have: the fallback path is INDISTINGUISHABLE from success.
`assets.get('models','spore_pod').geometry` was there and correct every time I checked it — I
was checking the loader, never the pool that consumes it. Measured after the one-word fix: pool
geometry goes 42 verts -> 6056 (spore_pod), and the new pathogens render at 2234 (Giardia) and
2500 (H. pylori).

Also in: the entity matcap the same investigation was written to prototype. emissiveMat takes an
optional matcap and applies it as LUMINANCE ONLY — sampled, reduced to luma, multiplied into the
core — so an amber foe stays exactly amber and a cyan commensal stays exactly cyan. The
ART_BIBLE readability law that the whole friend/foe system rests on is untouched by construction,
while the silhouettes stop being flat glows and start reading as lit, wet, three-dimensional
organisms. Switchable with ?matcap=0 for A/B, and absent-safe if D's matcap never shipped.
Weapons and pickups deliberately keep the flat look — a tracer is not a creature.

Net effect for playtest: this is the first build where the pathogens are actually visible as
pathogens. The GLB pipeline was correct for weeks; the wiring was one word short.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 18:31:14 +10:00
type-two
a318cf09f7 [lane B+C+F] The Appendix Sovereign: the secret boss, and a reputation you cannot fake
Two boss specs written to C's format (LANE_C_NOTES §Boss specs, round 3 additions) and the
first of them built. Both extend the roster along C's own law — one boss, one pressure — onto
the two axes the game had systems for and no boss pointed at: reputation (§4 Sovereign) and
restraint (§5 Mast Cell, specced only).

THE SOVEREIGN. The appendix is not vestigial: it is a biofilm reservoir where the gut's flora
shelter and re-seed the colon after a purge, and the whole fight is that fact. It is the only
boss whose existence is decided before you arrive — read once on entry:
  karma >= 70  THE COMMUNION — no fight at all. Full coat, a permanent phage escort. The
               reward for five levels of restraint is being spared a boss.
  karma >= 30  THE TOLL — P1 only.
  below        THE SOVEREIGN — three phases. P1's matrix is immune to the cannon and only the
               antacid torpedo disperses it (a mature biofilm's whole defining trait); P2 is a
               quorum bloom that re-armours when the room gets crowded; P3 is a 20s reseed run.
And the mechanic that makes it this boss: the lair is seeded with YOUR OWN FLORA, and it feeds
on dysbiosis — every commensal your stray fire kills re-armours a plate, live, mid-fight.

KARMA vs STANDING — a real bug found in testing, and worth the extra number. `standing` is a
SPENDABLE meter: a full bar is consumed to bud an ally, dropping to a 50 floor. That meant a
perfect player could never HOLD >= 70, so the Communion was almost unreachable — verified: a
symbiotic pilot showed standing 50 and was demoted to "toll". `karma` is the same inputs with
nothing spending it. The Sovereign's tier and gates' `secretTo` branches read karma; E's bar
still reads standing.

Also: boss.js generalised to a registry, which was the claim its header made — the Sovereign
needed zero changes to enemies.js. LS_appendix (3 segments, 800 units, 16 events), reached from
L4's ileocecal valve via `secretTo`/`secretNeed` — anatomically exact, the appendix hangs off
the cecum right past that valve. The secret is hidden behind conduct, not behind a wall.

C's selfcheck caught par.time as a free medal and the fix improved the design: par 160 sits
under the timid traversal, so only a Communion run (which has no fight) can beat it. The time
medal rewards the same conduct the level is about.

Verified: all three tiers branch correctly (communion = 0 nodes + 4 phages; toll = 1 phase;
full = 3 phases, 4 armoured); killing a commensal mid-fight re-armours a plate (reason
"dysbiosis"); the valve routes karma 100 -> LS_appendix and karma 0 -> L5. Levels 5/5 valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:25:36 +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
3c059f20db [lane B+C+F] The biofilm gate: the reputation cash-out, "it helps you in level 5" made real
The deeper payoff of the flora loop. A new `biofilm` level event: cross one and combat checks
BIOME STANDING — high enough (>= need), the native flora PART the biofilm, budding a wave of phage
allies + refilling coat, then spending the goodwill; too low, it stays sealed and you run the
gauntlet raw. Emits `biofilm {opened}` so E can voice both.

- combat/index.js: biofilmGate(ev) on the level:event pump (fields need/allies/coat).
- levels/index.js: the selfcheck knows the `biofilm` type now (validates need 0..100).
- L2: one demoed at s2500 — the opening flora/akkermansia/beacon let you BANK standing, and the
  gate cashes it into a phage escort right before the hiatus + reflux finale.

The mechanism is complete; L5 also gets the fast-route/Appendix branch when that level + world
support land. Verified in-engine: high standing -> opened, 3 phages, coat 50->90; low -> sealed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 00:16:12 +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
4bcb942312 [lane E] The biome meter: the reputation you're building, made visible
A teal BIOME STANDING bar joins coat/hull in the HUD (matches the thin-cyan line-work; scale-
only per the 60fps law). Hidden until the first `standing` event, so a flora-less level never
shows a bar you can't fill. Brightens near full to telegraph an incoming phage ally.

combat/index.js: rescaled standing to a clean 0..100 (tend +5, harm -35) — a FULL bar buds an
ally and drops to a 50 recharge floor, so the meter reads as "charge it to summon help." First-
pass numbers; tune to taste.

Verified in-engine: tending flora reveals the bar and fills it proportionally (0.20 -> 0.45),
teal, below coat/hull.

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