ade419839f
43 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ade419839f |
[lane B] Playtest fix: the ship was sluggish, and it measurably was
First human feedback: "ship movement feels sluggish". It was, and the cause was a bug rather
than a taste call.
`disc.damping` is applied every step AFTER accel, so the real cap on lateral speed was never
`maxSpeed` — it was accel/damping. At 78/6.5 that is 12 u/s in theory and a MEASURED 10.16,
which means the authored maxSpeed of 13 was unreachable and had been decorative since round 1.
A full tube crossing took 1.95 s. Against 12-20 u/s of forward flow that is 30+ units of canal
travelled while you cross the lumen once — so anything read late simply cannot be answered,
which is exactly what sluggish feels like from the cockpit.
accel 78 -> 155 (maxSpeed is now the ACTUAL cap: 155/6.5 = 23.8 > 19)
maxSpeed 13 -> 19 (crossing 1.95s -> 1.10s, 1.77x faster; ramp to top speed 0.12s)
damping 6.5 unchanged — it is what makes the ship STOP crisply, and precision was never
the complaint. Only the ceiling moved.
Knock-ons handled rather than left to rot:
- bank.gain 0.055 -> 0.04, so 19 u/s lands exactly at bank.max: full deflection still means
full roll and everything below it stays proportional. At the old gain the ship would have
been pinned at max roll almost permanently.
- arena thrust/maxSpeed 44/11 -> 90/16. Those were sized against the old disc; leaving them
would have made every room feel like treacle next to the corridor leading into it. Still
deliberately under the tube's flow, per the GDD's "slower, spatial".
Verified no tunnelling at the new speed: pinned at 19 u/s straight into the wall, the ship
reaches rho 10.51 against a wallRho of 11.38 and stays there — 0.32 u of travel per frame at
60 fps, well inside what collideWall resolves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
9f6e91fd8f |
[lane B+C] The Tapeworm and the churn: both deferrals closed, campaign ready for playtest
The two things I deferred out loud, built — and the second one turned out to be the primitive
the first was waiting on.
THE CHURN (the force primitive). Everything in hazards.js pushed the player RADIALLY; nothing
applied a tangential one, which is why both L3's cyclones and the Guardian's arena churn got
deferred. `churn` is now a hazard kind: a tangential impulse that REVERSES on a period, so it
cannot be leaned into, only re-read. Measured: 4.32 u/s of push, direction flipping.
- L3 s1100: cyclones spanning the sea, deliberately overlapping the first tide — the room
starts spinning you at the exact moment the safe air starts shrinking.
- The Pyloric Guardian carries its own (`def.churn` + per-phase `churnMul`), so C's spec is
finally honoured in full: "pushes you at the walls", x1.5 in P2, harder in P3. Measured
2.7 u/s on the player mid-fight. The boss.js header that said this was missing is corrected.
THE TAPEWORM (LANE_C_NOTES §2), the campaign's third pressure: a DISTANCE race. One new idea in
the schedule — `flee` — advances the boss's own s and drags its ring along at fixed offsets, so
6 proglottids string out 70 units down the tube ahead of you instead of sitting in a
cross-section. Fall 110 units behind and it is gone. Verified both ways: fall behind ->
"escaped" after 16.8s having seen only P1; keep up -> all three phases -> win. C's line was "a
chase you can lose without dying", and that is now literally the outcome table.
Placed at L4 s5460, immediately before the biofilm cash-out, so the chase and the reward land
together. L4's "no_miniboss_yet" note is replaced rather than left lying.
PRE-PLAYTEST SWEEP: 6/6 levels valid, all load with ZERO console errors, every level carries
exactly 3 biopsy samples, five bosses across the campaign (mast_cell / pyloric_guardian /
tapeworm / the_blockage / appendix_sovereign), 16,820 units of authored canal, and the chain
resolves L1 -> L2 -> L3 -> L4 -> L5 -> end with LS_appendix rejoining at L5.
Everything in the GDD's roster now exists except the numbers being right, and that is the part
only humans can tell us.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
d39c0b9358 |
[lane A] Procedural epithelium: a Voronoi cell pavement in the wall shader
Stolen from the shader half of Sebastian Lague's graphics-library video (the Voronoi farm-plot trick), because it turns out to be an anatomy tool rather than a decoration: mucosa at SEM scale IS a Voronoi diagram. Lane D's own oral prompt asks FLUX for a "cobblestone pavement of flat polygonal squamous epithelial cells" — that is a Voronoi described in English, and generating it in the fragment shader instead is free, never repeats, and can do the one thing a baked texture never will: BREATHE. Membrane width rides vPulse, the same wave the vertex shader displaces with, so the pavement compresses exactly where the muscle contracts. - F2-F1 Voronoi (9-tap), multiplied into the detail LUMINANCE, never added — D's texture stays the author of the look and this is structure underneath it. Applied to both the textured and the assets-optional fallback path. - Rides D's own tiling (duv * scale), so cells hold their physical size on a radius-7 hiatus and a radius-70 sea — the upstream arc-length fix does that work for free. - Compiled out with a define, not branched out on a uniform: a biome that wants no pavement pays nothing for nine hashes a pixel. Per-biome, because a pavement is a CLAIM ABOUT TISSUE and not a filter: oral 0.50 (squamous epithelium is the strongest case in the body), appendix 0.48 and colon 0.42 (biofilm mats are literally cells), stomach 0.38 (gastric pits), small intestine 0.20 and esophagus 0.18 (villi are projections and folds are folds — there it is a whisper that only breaks the texture repeat). Verified: per-biome values reach the material (esophagus 0.18, not the 0.35 default), the effect reads on the wall at gameplay distance, and world.hash() is byte-identical (bea3807c) before and after — this is pixels only, the determinism law is untouched. Not measured: frame cost on real hardware. Nine hashes per wall pixel is not free and the budget is 60fps on mid laptops; if it bites, the honest lever is dropping the low-gain biomes to 0 (which compiles it out entirely) rather than cheapening the shader. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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>
|
||
|
|
02e2387906 |
[chore] Drop the throwaway preview ports
Added while chasing a browser ES-module cache during verification (a fresh origin is the only reliable bust). They are workflow cruft, not project config — back to the four real ones. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
1e4775ab12 |
[lane C] L4 Small Intestine: the long maze — the campaign doubles in length
The first new level since L2, and the campaign's endurance beat. 7 segments, 6000 units,
63 events, ~10 minutes. Authored entirely from systems that already exist — no new engine work.
The three acts are anatomy, not invention: duodenal bulb -> descending duodenum (the ampulla,
where bile enters) -> jejunum -> deep jejunum -> ileum -> terminal ileum -> ileocecal valve.
Giardia really colonises the duodenum/jejunum and Peyer's patches really concentrate in the
ileum, so the difficulty curve IS the gut's own immune gradient: you fly from the quiet
absorptive end into the lymphoid end and the walls get angrier.
Identity vs L2: flow 8-10 against L2's 12-20, curviness up to 0.70, fog 0.050. L2 punishes bad
reflexes; L4 punishes bad reading. Segmentation ring gates (the small bowel's real motility)
teach at 70% open and test at 43% in an 8-unit lumen.
Three catalogue rows carry the new fiction onto existing archetypes — chyme_clump (floater),
neutrophil_hunter (seeker), bifido_colony (flora). The ileocecal biofilm gate at s5500 is the
first time the reputation loop pays out at a level's END rather than mid-level.
C's own tooling verifies it: selfcheck 4/4 valid, and --sim reports par 600s vs par-pace 576s
("par is reachable at par-pace throttle"), a pressure curve of 1.00/1.43/1.90/2.46/1.65/2.69/
1.63, min clearance 3.62 vs the 2.5 law, all 22 checkpoint gaps <= 29.3s. Driven end-to-end
in-engine: all six entity types spawn, biofilm fires at 5500, valve gate completes to L5.
The GDD's Tapeworm miniboss is not here — no boss framework exists yet. It goes in at ~5500
when that lands; the level notes say so.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
fb08d7895b |
[lane F] Chain the campaign: CONTINUE now loads the next segment
cards.js has been raising `ui:continue {from,to}` since Lane E round 1, with the comment
"INTEGRATOR: nothing consumes ui:continue yet." Boot consumes it now, so crossing a gate and
pressing CONTINUE loads C's `next` level instead of ending the run. Three levels in a folder
become a campaign.
Deliberately a full page load: the alternative is rebuilding world + player + combat + UI in
place and re-wiring every bus listener to save ~1s on a transition that happens 4 times a run,
and the reload guarantees no leaked pools or stale event pumps. Flags ride along (?dbg=1,
?seed= survive the hop). A `to` that isn't in CAMPAIGN — or is null — ends the campaign on the
medal card rather than dumping the player into the stub tube.
Verified: L2 -> CONTINUE -> ?lvl=L3_stomach with flags intact; unknown "L4_small_intestine"
and null both correctly no-op.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
8706387ff6 |
[lane B] The phage gets its wings: an iconic capsid-tail-legs silhouette, built from primitives
#3 of the microbe pass. The phage ally was a bare icosahedron; now it's the recognizable T4 "lander" — icosahedral capsid + tail sheath + six splayed leg fibres. I ran the requested hunyuan3d_mlx path first (flux concept -> hunyuan): it gave a great FLUX reference but, exactly as ART_BIBLE predicts for thin features, the mesh WEBBED the legs into a near-cubic 1.27x1.41x0.94 blob — no better than the icosahedron, at 217KB. So the phage is built PROCEDURALLY (phageGeo): merged icosahedron + cylinders, ~150 tris, deterministic, bold enough to read as a spidery phage at gameplay scale. The one microbe where geometry beats photogrammetry — its shape IS a platonic solid plus cylinders. (mergeGeometries needs all-or-none indexed; icosahedron is non-indexed and cylinders indexed, so each part is toNonIndexed()'d before the merge.) Verified in-engine: three phages read clearly as capsid+legs from head-on, side, and away. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
5b076aef54 |
[lane D] Skin the yeast: the first Trellis mesh in GUTS, through the freshly-wired GLB loader
spore_pod (the Candida foe) now renders a real 3D mesh instead of an icosahedron:
flux_local "budding yeast colony clump, fused knobbly spheres" -> bg_remove -> trellis_mac
-> Blender decimate 197k->3019 tris + strip materials (entities are flat emissive, only the
silhouette matters) -> web/assets/models/spore_pod.glb (213KB, texture-free).
First real GLB through the loader wired in
|
||
|
|
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>
|
||
|
|
464f5c8a18 |
[lane E] The game finds its voice: procedural synth bank, the WebAudio graph, the damage feed, and the medal/title/pause cards
Round-1 Lane E audio + the two remaining UI surfaces, consolidated from the JING5
clone onto main and made determinism-clean:
- audio/synth.js — procedural voice bank (primary path; audible with an empty manifest)
- audio/engine.js — WebAudio graph, cue router, bed, heartbeat; now the listener for the
11 gameplay bus events that were previously firing into the void
- ui/feedback.js — feed-corruption damage overlay (an ART_BIBLE law previously unmet)
- ui/cards.js — title / medal / pause cards; boot now honours ui:pause
- boot.js — mounts all five modules with per-module failure isolation and a
frame() tick (never step(), so stepped sims stay deterministic)
Determinism gate: threaded engine's seeded rnd (mulberry32 off ?seed=) into createSynth,
replacing every Math.random in synth.js. Audio texture is now reproducible per-seed and
qa.sh is green.
Verified: QA green; runtime smoke on ?seed=7 scheduled 182 osc + 91 buffer voices + 268
envelope ramps during play, zero console errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
0e2b5c10fe |
[infra] Stamp the deploy: curl /gutsy/VERSION to see what is actually live
I shipped a working tree earlier and had to tell John "live matches no commit in the repo" — which is exactly the sentence a deploy should make impossible. deploy.sh now writes the HEAD short-sha to web root as VERSION, and verifies it came back. `curl https://partly.party/gutsy/VERSION` answers "what is online right now" without trusting anyone's memory of the last deploy. The dirty check is scoped to `git status --porcelain -- web/`, not the whole tree, because only web/ ships: editing docs or this script does not make the payload unreproducible, and a stamp that cried wolf on every NOTES edit would get ignored. A dirty payload ships as `<sha>-dirty` and says so, loudly, rather than refusing — mid-round lanes need to push a look at something without committing first, and a deploy tool that blocks that will just get bypassed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
31cedb2f60 |
[lane A] Adopt an orphaned evidence shot: the player view, flying into black
Not my lane's file. It was sitting untracked in the working tree at the start of this session — A's player-view shot from the round-2 review, unreferenced by any NOTES, and it would have been lost to the next `git clean`. Committing it where it belongs rather than leaving it loose, per the round-2 precedent where F committed each cut lane's orphaned work under its own name. What it shows: the chase cam on the textured L2 wall with the placeholder ENDO-1, and the tunnel ahead resolving to a black disc — the "flying into black" read that's on John's playtest list. A's fog/void tuning is the subject. PNG, 1.5 MB. Ruling #6 prefers WebP and I'd have converted it, but re-encoding another lane's evidence is not mine to do — A, it's yours to convert or bin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
77ddf88c31 |
[infra] GUTS is live at partly.party/gutsy — deploy wired, ship-check clean
README said "target: partly.party, not wired yet". It's wired. tools/deploy.sh follows the GAMES doctrine (deploy-map skill): QA gate -> rsync web/ to VPS staging -> docker cp into forum-nginx -> verify. No build step, so web/ ships as-is: vanilla ES modules + importmap with three r175 vendored, all paths relative or resolved from import.meta.url, which is why it runs from a subdirectory with no base rewrite. 6.6 MB. nginx.conf needed no edit — its catch-all `root /usr/share/nginx/html` already routes any new directory, which is how blobbo works. /gutsy (no slash) 301s to /gutsy/. Excludes web/dev/ (ruling #5: never shipped). Verified live, not assumed: the lane harnesses 404 through to the arcade index. THE 200 THAT ISN'T. That same catch-all try_files means ANY missing file returns the arcade's index.html with HTTP 200. A deploy that lost every module would still curl 200 on all of them. So every check asserts on CONTENT, never on the status code — this is the failure deploy-map records as ".bin data URLs serving arcade HTML". The guard caught itself, too: it originally grepped for `<title>MonsterRobot`, but the arcade's real title is `~*~W3LC0M3 2 TH3 M0NST3R R0B0T P4RTY 4RC4D3~*~`, so it could never have fired. Matched against the live box now. DURABILITY, and it's John's call. This docker-cp's into the container's own layer, matching blobbo/glytch: zero downtime, nothing else touched, survives restarts and reboots (restart: unless-stopped) — but NOT `docker compose up --force-recreate` or an nginx image bump, which wipe it. Re-running the script is the fix. The durable alternative is a bind mount in forum/docker-compose.yml like cratewars/roguelike have, which costs a forum-nginx recreate and brief downtime across ALL of partly.party, so this script does not take that decision. ship-check (README says run it before deploying — I ran it after; my miss, and it came back clean): 1 auth N/A — static files, no endpoint/API/admin surface 2 secrets PASS — nothing secret-shaped in the payload or the diff 3 input/SSRF PASS — the only user string reaching a fetch is ?lvl=, and C gates it on the CAMPAIGN allowlist BEFORE the load (levels/index.js:151). Traversal tested live with `curl --path-as-is` (plain curl normalises ../ away and proves nothing): /etc/passwd never leaked. DEPOT_BASE in assets.js is unused — the manifest has zero external URLs. 4 money N/A 5 deploy PASS — verified by content + booted in a real browser: assets.misses() == [], 15 draws, 69k tris, zero console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
83c30bf344 |
[lane E] The instrument exists: a HUD, and a surgical team who talk to you
#ui had been an empty div since round 0 — E never ran. It has an owner now. HUD (charter #1): coat/hull, heat, torpedo pips, speed/flow/throttle, boost, score, combo, biome + segment name, and a progress rail with C's 10 checkpoints ticked. Bus-only — zero imports from B, exactly as the charter asks. player:state and combat:state arrive every frame and ARE the clock, so the HUD cannot tick while the game isn't running. 60fps law: DOM is built once; the hot path is transform + textContent-on-change only. Bars scale (scaleX), they never resize — a width:% write per bar per frame is a layout pass. Torpedo pips rebuild only when ammoMax changes, not when ammo does. COMMS — on nobody's list. Argued for, then built. The GDD says "Star Fox arcade flow" and there was nobody in the game but the player. Three-hander: voss (attending, terse), park (resident, over-excited), adeyemi (immunology, apologetic — and amber, the colour of the cells attacking you; his fault). Deterministic round-robin per trigger key, no RNG. Priority ladder + per-key cooldowns so the crew shuts up during a firefight. It turned out to be more than flavour: it is the tutorialisation channel the GDD asks for and L1 has no other vehicle for. The hazard lines teach what C's HAZARDS registry note says the hazard IS ("ring gate. it opens on a beat — match the beat, do not ram it"). Keys are the real HAZARDS ids, checked against levels/enemies.js rather than guessed — all three L2 kinds get bespoke lines, zero generic fallbacks. A level's worth of dialogue is now a data edit in LINES. Three defects, all mine, all found by the stepped sim, none visible to the eye: - The progress rail was dead. I read `level.length` — a field that does not exist. The level carries segments, not a total. It now uses the runtime world.length that B already divides into p.progress. - My clock was fiction. Comms counted `+= 0.1` per timer tick and called it seconds; a backgrounded tab throttles setTimeout to ~1s, so lines held ~10x too long. It now reads performance.now(). - Every hazard warning had the LOWEST priority in the game. PRI has a key `warn`, but say() is called with `warn_reflux_surge`, so PRI[key] was undefined and fell through to the default of 1 — the surge screaming "GO. GO." could be talked over by park admiring a specimen. Keys now resolve exact-then-family. The evidence shot IS the fix: same seed, same frame, the warning now sits where the checkpoint line used to be. ### -> Lane B — player:state stops emitting the moment you die player.js:103 early-returns before the emit when !st.alive. So `alive` is unobservable (E can only ever read true — the field is currently decorative), and E cannot use player:state as a clock, because it stops exactly when the death UI needs to run and through boot's whole 2.0s respawn window. Comms works around it with its own wall-clock. The death/respawn feed-drop (charter #4) will not be able to. Ask is in NOTES; not patching your file. ### -> Lane F — two contract items I edited boot.js (~10 lines mirroring your own dynamic-import/try-catch/ console.info fallback) because #ui needed an owner and two modules nothing constructs aren't worth much. Ratify or move it. Second: shot_sink.py cannot photograph Lane E — it POSTs renderer.domElement.toDataURL(), and the HUD is a DOM overlay, so the house evidence tool renders it invisible. This shot is a composite (canvas -> 2D canvas, then #ui via an SVG foreignObject). Offer to lift it into pipeline/ as DBG.shotUI() stands. qa GREEN. Evidence: docs/shots/laneE/round2_hud_aortic.webp (s=1111, seed 7, live hazard warn + x6 chain). Dev server guts-e on 8146. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
a99f733452 |
[lane A] MODELBEAST dresses the molecules: 5 matcaps, and the canal picks space-filling
The right axis wasn't "generate or don't" — it was WHICH HALF. Geometry stays procedural because a molecule's shape is already known exactly. **Material is exactly a generation problem**, and it's the whole difference between a textbook diagram and a game object. So MODELBEAST made the materials. 5 greyscale matcaps, flux_local, 69 seconds, $0.00, 68 KB total: mol_glass (O,N) — the big win: oxygen is a red glass marble with internal glow mol_matte (C) — graphite soot; carbon reads as the scaffold it is mol_chrome (Co) — B12's cobalt is now a chrome bearing in a cage mol_molten (P) — ATP's phosphate tail is incandescent AND pulses (uTime, free) mol_pearl (H) — satin white; hydrogens stop shouting The trick is Lane D's own law applied to spheres: author the LUMINANCE, tint in-shader. derive_maps already greyscales every matcap, so one glass ball serves oxygen AND nitrogen at their own CPK hues — 5 balls dress the whole periodic table we care about. They pack into one strip atlas indexed per-vertex by aMat, so **a molecule is still exactly 1 draw call** however many materials it's made of (7 molecules = 7 draws, unchanged). ?matcap=0 falls back to the built-in fake-lit path: the assets-optional law holds. SPACE-FILLING WINS FOR PICKUPS, and the canal decided it, not me. New `repr: 'space-filling'` draws every atom at its real van der Waals radius with no sticks. A/B'd at real pickup size against the real L2 wall, same camera: ball-and-stick goes spindly and dissolves; the solid blob holds its silhouette — and it's CHEAPER (21k vs 26k tris, no bond cylinders). Recommendation to B: space-filling in flight, ball-stick for hero/UI/collect close-ups where the chemistry is worth reading. Evidence: round2_molecule_materials.png (3-way). Two defects found by rendering it, both mine, both fixed: - Tint x luminance darkens TWICE: CPK carbon is 0.4 and a matte ball averages 0.5, so soot x soot vanished — the first pass sank glucose, caffeine and the cobalt into the background. Floored the matcap at 0.22, the same floor the fallback's key light always had. - The molten matcap was my own bad prompt: I asked for an "incandescent white hot CORE" and got exactly that — a black ball with a hot spot, so phosphorus lost its orange. Re-rolled for the whole sphere to glow (attempt 2 of the <=2 PIPELINE allows). Lesson for the kit, next to the organ/tissue one: a matcap is a LUMINANCE LOOKUP, so a dark-dominant ball makes a dark-dominant object. Also corrected the doc's own framing — v1 said "I'm authorised to burn GPU and I'm not going to", which was the wrong axis, not just the wrong tone. qa GREEN; 16/16 texture provenance verified (synced the FIXED batch json up first this time, so the box couldn't clobber it on the way back). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
9f42a2e9ab |
[lane A] Molecules as pickups: real gut chemistry, procedural, 1 draw each
PROTOTYPE + proposal, not wired into boot. Pickups are B's systems, their economy is C's, object art is D's — this is a renderer and an argument, offered to all three. Bench: web/dev/laneA_molecules.html · docs/MOLECULES.md THE PITCH: the pickups already in L2 ARE molecules. C authored `nutrient`, `mucin`, `B12` and `antacid ammo` before any of this existed — those are the real biochemistry of a digestive tract, and glucose/cobalamin/bicarbonate are real compounds with known shapes. We don't need to invent a pickup language; we need to stop hiding the one the fiction handed us. GUTS is already a science game (an eosinophil is a real white blood cell, candida a real yeast, the hiatus a real choke point) — it just doesn't LOOK like one, because everything on screen is tinted tissue. THE RULE THAT MAKES IT WORK: CPK is the scanner. Everything identified is drawn in the standard element palette (O red, N blue, P orange, metals pink); everything else stays monochrome tissue. Against six biomes of tinted wall a CPK molecule is the ONLY saturated foreign colour on screen — it reads as artificial, valuable and targetable with NO HUD marker. Proof: round2_molecules_in_canal.png. That's the ART_BIBLE's synthetic-scanner fiction paying rent, and it hands Lane E a free HUD palette. WHY PROCEDURAL AND NOT MODELBEAST — and I'm authorised to burn the GPU: a molecule's shape is already known exactly. Glucose is a hexagonal ring because it IS one. FLUX+TRELLIS gives a plausible blob in 3-8 min that a chemist clocks as wrong and that can never be re-derived. Ball-and-stick from an atom list is exact, rebuilds in ms, and is ONE DRAW CALL per molecule (measured: 7 molecules = 7 draws / 26.5k tris). The split is a principle: small molecules = procedural (exact geometry known); proteins/enzymes = MODELBEAST blobs (real ones are 3000-atom blobs nobody reads — and PIPELINE says blobby organics are TRELLIS's sweet spot); cells/creatures = MODELBEAST, D's lane, unaffected. BUILT: water (chaff) · bicarbonate (antacid ammo — HCO3- + HCl is the real neutralisation, it genuinely fizzes CO2) · glucose (nutrient/score) · caffeine (overdrive — really gut-absorbed, needs no tutorial) · ATP (boost — the orange triphosphate tail IS the charge, a pickup that's a diagram of its own mechanic) · capsaicin (burn hazard — long greasy tail reads wrong on sight) · B12 (rare treasure — a cobalt in a corrin cage, the only metal vitamin; C authored it as a pickup already). Size is a free rarity signal: `unit` mode keeps TRUE relative sizes, so water is a speck and B12 a chandelier. Found by rendering it, which is why the bench exists: fuseRing returns vertices starting adjacent to B, and bonding them from A instead draws a chord across the ring — still a valid 5-cycle so nothing errors, it just looked like a squashed pentagon. Fixed in caffeine + ATP's adenine, adjacency documented. Stated plainly: geometry is idealised (correct connectivity/rings/bond orders, believable angles, authored flat) NOT crystallographic — real glucose is a chair, not a hexagon. The B12 is a simplified corrin core. Untested above ~7 on screen; fifty would want instancing. qa GREEN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
111ade7d12 |
[lane A] The swept room + the acid sea, built — and it corrected me four times
The shape read in LANE_A_NOTES was written from reading code, so I built it to
find out if it was true. Flyable: web/dev/laneA_world.html?dbg=1&assets=1&lvl=swept
Nothing here is imposed on anyone: every addition is additive and a level that
opts out behaves exactly as today. C can bin the whole proposal and lose nothing.
WHAT THE PROTOTYPE DISPROVED (my own cost estimates, all four):
- "radial resolution must scale with radius" — wrong. 64 segments is a 0.05u
circle error at r=70. The arena needs it because it fbm-displaces; the tube's
wave is smooth in theta. No change made.
- "cap geometry at the span ends" — a thinko. The canal is continuous; a room is
a fat part of it. Only the fundus dome caps, and it stays an icosphere.
- cost — cheaper than feared: 9 draws / 98k tris worst-frame for a radius-70
cathedral vs 8 / 74k for the L2 corridor. No leak over a full sweep.
- "one schema flag" — held. `segments[].mode: "open"` is the entire ask.
THE TWO REAL COSTS, NEITHER PREDICTED:
1. D's `tile[0]` is a COUNT, not a density — only a texel size if radius never
changes. Measured 6.1:1 smear at r=70. The wall now derives the count from
each ring's own arc length (aRadius attr + uThetaSpan); D's authored numbers
keep their exact meaning at their reference radius, no re-authoring. That fix
then laid a seam down the canal (a fractional count can't wrap) -> rounded to
an integer, seam gone. Not a no-op on corridors: L2's hiatus takes 3 repeats
where it took 4, which is the texel size correctly holding still as the pipe
narrows. Eyeballed.
2. MY OWN PINCH GUARD WAS WRONG and would have blocked C. The fixture scored
1.30 and "failed" the >1.5 law; it was never bent. stats() divided the
tightest turn ANYWHERE by the widest radius ANYWHERE — 996 units apart, a
bend in the radius-13 pylorus against the radius of the sea. Locally its
worst point is 6.4. pinchRatio is now min over s of turnRadius(s)/radius(s).
Provably one-directional (global <= local by construction) so nothing that
passed can fail; real levels GAINED headroom: L2 3.32->4.16, L3 1.91->6.99,
L1 2.11->6.06. This mattered — the guard is what backs the round-1 promise
that C never has to think about curvature.
THE ACID SEA (world/acid.js, `level.acid {from,to,height,biome}`):
Flat emissive #c8ff3a, level and NOT tube-following — that's the mechanic. The
best find of the session: `height` is ONE NUMBER that tunes the level. Measured:
-18 -> 0% of the sea floor is dry shallow, -55 -> 14%, -62 -> 48%. So C's "position
IS the resource" falls out of the radius wobble they already author — the mucus
shallows are just "where the wall rises above the waterline", nothing to place —
and rising acid literally drowns them: free escalation, same scalar their event
pump drives. Also disproved my own claim: the centreline stays level (+/-1u over
700u), so shallows come from radius wobble, NOT the canal's bends. C must author
vertical bends deliberately if they want depth by anatomy.
Stated plainly, not papered over: the waterline is prototype quality (the plane
meets faceted rings and the shoreline steps). Acid does not drain the coat —
depthAt(pos) is the hook and the cost is Lane B's call. Nothing drives height but
DBG until C's events do.
qa GREEN incl. the new provenance gate; spline selfcheck green; L2 corridor
re-eyeballed for regression. Rulings requested from F in NOTES §-> Lane F.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
3a6dbc30a3 |
[lane A] Complete the biome texture set (oral + colon) + a provenance gate
Generated on MODELBEAST flux_local, on-device, $0.00. Lane D's pipeline used
unchanged; **D owns these — ratify, retune or bin them.**
Why A did D's job: the six-biome tint contact sheet could only judge 3 of 6
biomes for real (oral/large_intestine/appendix had no pack and fell back to
procedural), and you cannot ratify a tint against a stand-in. All six are now
textured — the set ART_BIBLE §Biome palettes promises.
- wall_oral_a/b, wall_colon_a/b + normals. Seams 6.35/9.30/14.06/6.37 -> 1.01/
1.08/0.74/1.36 (D's "indistinguishable" band; the shipped pack's weakest is
2.39). Means normalised to ~0.50 by D's derive_maps, untouched.
- appendix wears the colon pack at its own gold tint (TEXTURE_SHARE in
world/index.js) — anatomically it IS colon tissue, and it's ART_BIBLE's
"one texture, two biomes" law for zero bytes. Distinct from the temporary
slug map; keep it when that dies.
- oral_a took three subjects. v1 named an ORGAN ("tongue") and FLUX drew the
organ's silhouette — D's "one urchin of villi" in a field's clothing. v2 named
the tissue but papillae are 3D projections, so perspective gave it a vanishing
point that tiles into starbursts. v3 asks for squamous cell pavement: flat by
construction. Rule for the kit: prompt the SURFACE, not the FEATURE.
Evidence: docs/shots/laneA/round2_oral_prompt_evolution.png
PROVENANCE DEFECT FOUND AND FIXED (-> Lane D):
batch_textures.json recorded, for 4 of 6 walls, the prompt from the framing
experiment D tried and rejected — not the prompt that made the shipped pack.
record() only runs on generation, so reverting a prompt while keeping its image
(exactly what D correctly did: "four winners kept untouched rather than churned")
drifts the record silently. Proved by regenerating esophagus_a at seed 1101 under
both prompts: the code's prompt reproduces the shipped wall, the recorded one
gives the bland wood-grain D described rejecting. Evidence:
docs/shots/laneA/round2_provenance_probe.png. Records re-pointed at the code's
prompt (images untouched — they were never wrong).
New: gen_textures.py --verify-provenance + qa.sh gate 5b, so it cannot rot again.
qa GREEN incl. the new gate; 11/11 provenance verified; all six biomes eyeballed
in engine (docs/shots/laneA/round2_tint_*.png).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
||
|
|
ecf43940ec |
[lane A] Round 2 close-out: colorspace re-eyeball, stomach shape read, ruling #5
Finishes the lane A round-2 list that
|
||
|
|
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> |
||
|
|
2e85ec0622 |
[lane C] Round 2 WIP (session cut off): schema v2, surf-aware pacing, chase analysis, --probe
Landed before the cut: schema v2 (segments[].wave.amp override, validated — breathe stays biome-owned); hiatus re-authored tight AND calm (radius back to 6.5, amp 0.6, 3.3u steady clearance); par 210 -> 180 with full rationale (below throttle-only par-pace, above a surf run — the medal teaches the mechanic); finale surge 21 -> 24 so surfing is the stylish escape (required 1.20x); surf profile reads A's live CREST_FACTOR; chaseAnalysis() + escapability laws in validate; NEW --probe builds A's real canal and measures clearance against the model (all L2 segments clear the 2.5 floor, hiatus 3.75); isMain guard + importSpline shim — fixes A's selfcheck process.exit killing C's mid-run (qa.sh had been green on one level out of three). Cut off before: fly-through of the integrated build (blocked on B), pickup economy handover (B proposed numbers in balance.js meanwhile — reconcile), L1 encounter design doc, GDD boss folding, NOTES. Committed by F to protect the shared tree; levels selfcheck 3/3 GREEN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
f0982e70c3 |
[lane A] Round 2 WIP (session cut off): crest-speed law, taper blending, schema-v2 wave, TBN walls
Landed before the cut: crestSpeed(s) + CREST_FACTOR 1.6 (ruling #1) with selfcheck asserts; radius blend widened +/-12 -> +/-25 (C's #4 dependency) + no-cliff selfcheck; per-segment wave.amp override as per-vertex aWaveA (ruling #8); colorspace law in the wall shader (ruling #2); TBN normal maps + matcap + dual detail layers (D's perturb(), trap documented in-shader); sample(s, out) v1.2; slug map shrunk to the two real mismatches. Evidence: docs/shots/laneA/round2_L2_*.png. Cut off before: NOTES/progress, stomach-arena shape read for C (task #7). Committed by F to protect the shared tree; spline + qa selfchecks GREEN at commit time. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
4ccf5edcc0 |
[lane F] Round 1 integration + ROUND2 instructions: the game is a game
Wired B's player/combat + event pump, D's assets, C's level pick into boot. Fixed qa ESM gate (node --check no-op, found by B) + added spline selfcheck. TECH: world contract FROZEN v1.1, bus events ratified, colorspace + crest-speed laws (CREST_FACTOR 1.6 — B proved surfing lost to throttle at 1.0). Stub complies. Verified integrated build via 60s stepped sim: 9 draws, 0 errors, 0 asset misses; one known gap (fiction-id spawn resolve) confirmed and assigned as B's top item. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
efea7d98f3 |
[lane D] Round 1: first texture pack + audio proof, exact-tiling pipeline
Textures (6 walls + normals + wet matcap, $0, ~64s on the m3ultra MPS): - FLUX ignores "seamless tiling" (seam_error 3.7-17.9); a 4-way cosine partition-of-unity blend of the four half-rolls fixes it exactly -> 0.87-1.32 - levels-normalised the pack to one exposure (means were 0.18-0.32); these are detail maps multiplied into a biome tint, so per-prompt exposure read as a broken tint rather than as dark tissue - prompt-kit experiment rejected + recorded: PROCITY's "uniform coverage" clause in the STEM cured centred subjects but flattened the pack; framing words now live only in the two subject prompts that needed them Audio (1 bed + 4 sfx, 4.2s render, 0.61/10MB, ogg+m4a dual-ship): - bed-esophagus 24.000s, loop seam 0.32 (wrap step 3x smaller than inner step) - cascaded lowpass poles: one-pole at 900Hz left 16kHz only ~25dB down = hiss - rewrote the spectrogram sheet (per-clip peak, log freq) — the first one was a saturated rectangle, and evidence you can't read is not evidence assets.js: miss ledger + misses() — Lane A found that a drifted slug falls back procedurally forever with no error. Each distinct miss now announces itself. Also: shot_sink.py (canvas -> docs/shots/laneD, correctly named) and a dev texture viewer that imports the stub world read-only for the eyeball law. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
689c478a7f |
[lane A] Round 1: the canal v0 — spline, streaming tube, scanner wall, arenas
Implements THE WORLD CONTRACT (TECH.md) in web/js/world/; drop-in for the stub. boot.js now runs this on C's L2_esophagus (3600u, hash cefc4f83), zero console errors. - spline.js: centreline + arclength LUTs + parallel-transport frames + radius law + peristalsis phase + project() + hash(). No three.js import, so qa can run its 14-assertion selfcheck headless. - curviness is solved from a curvature budget, not authored as an amplitude: the pinch guard caught the tube folding through itself on a wide+curvy join (turn radius 9.9 vs tube radius 17.2). C can no longer author a pinch. - peristalsis: global OMEGA, k(s) = OMEGA/flow(s), phase K(s) = integral k ds. A crest therefore travels at exactly the local flow speed, so "surf the crest" == "ride the current". Esophagus wave amp 0.9 -> 1.4 (measured): moves wallRho for Lane B. - tube.js: chunked streaming, seams aligned to biome joins, arenas own their s-span. - arena.js: displaced icosphere shells v0 (three's polyhedron detail is 20*(detail+1)^2, not 20*4^detail — 720 tris was far too coarse); fog sized to the room, since biome fog tuned for 100u corridors renders C's 360u acid sea as a black rectangle. - index.js: prefers assets.texture() over get() (get returns raw JSON a shader can't eat), plus an explicit slug map — D's wall_smallint_a vs biome id small_intestine would miss silently forever under the assets-optional law. Measured (C's real L2 + D's real pack, 1920x1080, renderer.info): 8 draws worst frame (budget 150), 74k tris (500k), no geometry leak across a full sweep, <120ms load, pinch ratio 3.32. fps not claimed — the screenshot pane runs tabs hidden, pausing rAF. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
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> |
||
|
|
1b5ec767cd |
[lane C] Round 1: schema v1, levels registry + selfcheck + pacing sim, L2 vertical slice
Data spine: - Schema v1 ratified (TECH.md, C-owned). Additive over v0: schema/par/next, segment name, cross-section placement (theta/rho as a FRACTION of safe radius), spread, span, pickup+gate event types, hazard params. STUB_LEVEL synced (that const only, per ROUND1 §Lane C.1) — world.hash() unchanged (1bdf001), geometry provably untouched. - levels/index.js: registry + loader (node+browser, no THREE), real schema selfcheck (qa's --selfcheck hook is now live) and a pacing simulator (--sim). Consumes the other lanes' contracts live rather than restating them: A's biomes.js for ids and wave.amp+breathe, A's SKIN, B's tuning.js hull radius, B's balance.js for scores. - levels/enemies.js (fiction id -> archetype join table), levels/difficulty.js. L2_esophagus — the vertical slice, complete: 3600 units, 7 beats, 47 events, 65 enemies, 10 checkpoints, 3 samples, reflux-surge finale. L1/L3 skeletons (shape only). Anatomy is the level designer: the three real esophageal constrictions are the three setpieces, and all three enemies are real esophageal pathology (bolus chunk, eosinophilic esophagitis, candida esophagitis). Measured (node web/js/levels/index.js --selfcheck|--sim; qa GREEN): - selfcheck 3/3 valid, exit 0. - pressure curve 0.27 -> 2.14 -> 4.35 -> 0.69 -> 4.53 -> 6.35 -> 4.40 (breather/pressure/ spike/breather/pressure/peak/finale). - par 3:30 vs measured 2:47 speedrun / 3:19 par-pace / 4:22 timid; par.score 9000 of a 9400 kill budget (95%), computed from B's scores. - all 10 checkpoint gaps within the <=30s death-cost law (max 28.8s); min clearance 2.73. Two contract collisions found and resolved (LANE_C_NOTES): - Biome ids: authored mouth/pharynx/cardia/pylorus; A's registry has six canonical ids and rejected all four. A is right — biome = look, segments[].name = anatomy. Conformed. - Balance: my first enemies.js restated hp/size/speed/score that B owns in balance.js. Cut to a join table; index.js reads B's scores via scoreFor(). Caught live: A raised esophagus wave.amp 0.9 -> 1.4 mid-session, making the diaphragmatic hiatus unflyable (2.33 vs the 2.5 floor). Widened 6.5 -> 6.8. The invariant also failed my own aortic constriction's first draft (radius 7, symmetric squeeze) -> radius 9, directional. No in-engine flythrough: ?stub=1&lvl= does not compose (snippet offered to F) and the stub is single-segment, so it could only render beat 1. The pacing evidence is a kinematic model, not a playtest — it proves L2 is flyable, paced and survivable, not that it feels good. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
dade036f69 |
[lane D] Round 1: pipeline scripts + assets.js loader
Ports PROCITY's pipeline shapes to GUTS (dry-run first, idempotent, resumable): - gen_textures.py: flux_local wall/matcap pack, ART_BIBLE prompts, provenance JSON - derive_maps.py: exact-tiling (4-way cosine partition-of-unity) + Sobel normals + WebP - build_manifest.py / validate_manifest.py: catalogue-what-exists + qa hook - gen_audio.py: procedural numpy synth, seamless loops, ogg+m4a dual-ship - js/core/assets.js: manifest loader, null-on-miss, ?localassets=0 empty boot Generation runs next on the m3ultra box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
34f84ab162 |
[lane F] Round 0 scaffold: docs, contracts, lane charters, bootable stub world
GDD v1 (flow-locked hybrid movement, 5 biomes + secret, boss roster), TECH contracts (world API, level schema v0, bus events, manifest law), ART_BIBLE (synthetic scanner look), PIPELINE (MODELBEAST-first, fal gated), PROCESS (PROCITY lane/round law), charters A-F + ROUND1 instructions. Seed code: shell + boot + core (rng/bus/flags) + stub world (peristalsis shader tube, 1 draw / 102k tris, contract-complete) + qa.sh (GREEN). Verified in-browser; evidence docs/shots/laneF/round0_stub_tube.png. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |