Lane E S18: [E] THREADS — the tier, the palette hunk, and the gate-5 seam for A
Two entries at the tail. Gate 4: the premise correction (S16 already shipped the glasshouse; the gap was that its $320 could not be reached), the asset, the tell measured twice, the 0.80 rung with the reasoning beside it, the five controls, the determinism result, the Blender 5.1.2-vs-5.0.1 finding, placement facts and the wreck apron, plus the editor palette hunk verbatim for A and the flag that the tier is placeable — with the one-variable law restated as A's to enforce. Gate 5: my half (two author-time pins) and the exact wiring A needs — why renaming the structures' keys alone makes it WORSE (the anchors take their key off the baked GLB node, so both stop matching and both corners become free), the two world.js hunks and the validateSite refusal, and what it buys: D's $115-vs-$45 split closes to $115 vs $90, leaving exactly the gnome's $25 as A's ruling rather than two unknowns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0a279b36f3
commit
162b78c3e1
265
THREADS.md
265
THREADS.md
@ -9505,3 +9505,268 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
|
||||
**Deploy still owes one hand:** tools/deploy_hardyards.sh is permission-gated in the
|
||||
integrator's session (ssh + docker to the games VPS). John runs it or green-lights it;
|
||||
S18 must not ship before S17 is live, per the standing one-sprint rail.
|
||||
|
||||
[E] 2026-07-25 — 🪟 **GATE 4 LANDED: THE GLASSHOUSE TIER — and the gate's premise had already moved.**
|
||||
**READ THIS FIRST: SPRINT16 ALREADY SHIPPED THE GLASSHOUSE, AND GATE 4'S THREE LITERAL ASKS
|
||||
WERE ALREADY GREEN.** `glasshouse_01` / `_wrecked` are built, exported, in A's palette, and
|
||||
pinned by e.test since S16 gate 5: fragility readable at a glance (translucent panes, pinned as
|
||||
a MATERIAL fact so a repaint that turns the glass opaque goes red), $320 against the gnome's $25
|
||||
(12.8×), and a wreck that EMPTIES instead of falling (aluminium frame at full height, 34 shards
|
||||
thrown wider than the footprint). I checked before building anything, because the cheapest way to
|
||||
ship nothing is to rebuild what exists.
|
||||
**THE REAL GAP WAS WRITTEN IN MY OWN TEST FILE, IN ITS OWN WORDS** (e.test, S16, above the apex
|
||||
pin): *"nothing in the sim wrecks a glasshouse yet — the value is inert data"*. `main.js:877`
|
||||
bills collateral off a **broken rig corner's** baked key (`world.anchor(c.anchorId).collateral`),
|
||||
and a glasshouse correctly offers **no anchors** — the one rule pinned from every direction, and
|
||||
it should stay that way. So the most expensive object in the game **could not cost anybody a
|
||||
cent.** That is RULE 2's mirror image: RULE 2 catches a key with no price; nothing caught a price
|
||||
with no key that can reach it. **It does now, and that assert is the gate.**
|
||||
**WHAT DESIGN.md LINE 179 ACTUALLY ASKED FOR:** *"Jobs: hail nets and mesh sails rigged OVER
|
||||
glass, where your own failure is the worst debris of all."* You do not tie to the glasshouse. You
|
||||
tie to the frame that STRADDLES it. So the tier is one new asset pair, and the glasshouse itself
|
||||
is untouched (zero churn on both its GLBs — the control that proves I only added).
|
||||
|
||||
**`hail_net_frame_01` / `hail_net_frame_01_wrecked` — 328 / 480 tris, 3.42 × 3.02 × 2.77 m.**
|
||||
A galvanised-then-powder-coated portal frame: two portals clear of the gable ends (±0.30 m), legs
|
||||
clear of the long walls (±0.45 m), head beams at 2.75 m so a net clears the 2.28 m ridge, eave
|
||||
rails, a thin tensioning wire down the middle, and the hail net **rolled** along one eave —
|
||||
rolled, because the JOB is to rig it, and a deployed net is a finished yard.
|
||||
|
||||
**THE TELL IS A POSITION, AND THAT IS THE NEW THING.** Every other trap in this game punishes you
|
||||
with a bad number on the object: the carport beam at 0.22, the fascia at 0.35, the corroded post
|
||||
at 0.55 whose real capacity you cannot read. **This frame's numbers are GOOD** — engineered
|
||||
portal, four real footings, welded factory eyes — and it is the most dangerous anchor in the game
|
||||
because of WHERE IT STANDS. So the tell is pinned as GEOMETRY off two shipped GLBs, not as paint
|
||||
on one: the frame's plan footprint **strictly contains** the glasshouse's on both axes
|
||||
(x ±1.71 vs ±1.32, z ±1.51 vs ±0.98) and its head **clears the ridge by 0.47 m** (2.766 vs
|
||||
2.30), and all four eyes sit **above** the ridge. "It stands over the glass" is a measurement.
|
||||
**THE 20 M READ IS METALNESS, NOT LIGHTNESS — S17's lesson, applied deliberately this time.**
|
||||
S17 measured that galvanised (metalness 0.90) renders **near-black** at yard distance because a
|
||||
near-mirror with no environment map has nothing to mirror — rgb(49,51,49). So every steel the
|
||||
player is allowed to trust is DARK. This frame is powder-coat, i.e. **paint**: metalness 0.05,
|
||||
diffuse, base luminance 0.903 against galvanised 0.734. **Measured off the shipped GLB, then
|
||||
LOOKED AT in the game's own renderer** (`tools/assetcheck/look.html`, cam at eye height 1.7 m):
|
||||
at 20 m the honest sail post is a near-black line, the frame is a **white cage**, the glasshouse
|
||||
is visibly see-through. At 11–16 m the rolled net reads as a hard **dark bar** along one eave
|
||||
(lum 0.254) — which Blender's own contact-sheet view transform had washed out to near-white, the
|
||||
jacaranda lesson again: a colour judgement made in Blender is a judgement about Blender.
|
||||
|
||||
**THE RUNG — 0.80. I OWN IT AS A PROPOSAL, A RULES.** Ladder: 0.22 carport beam < 0.30 carport
|
||||
post < 0.35 fascia < 0.45 swing frame < 0.55 corroded post < 0.65 pergola < **0.80 hail_net_frame**
|
||||
< 1.00 sail post. Two halves to the argument:
|
||||
· **It EARNS the highest rung below the post.** A pergola is a deck screwed to a house and it
|
||||
flexes; the swing frame stands on grass; the corroded post's capacity is unknowable. This is an
|
||||
engineered frame on four footings and **the only structure in the palette that was built to be
|
||||
tied to.** De-rating honest steel for looking dangerous is the corroded post's lie inverted.
|
||||
· **It is not 1.00 because of SECTION, not condition.** 32 mm sized for knitted hail mesh —
|
||||
~0.2 kg/m² and near-zero drag, because mesh *spills* wind. A 25 m² impermeable sail in a
|
||||
southerly is an order of magnitude more load. 0.80 is a 20% de-rate for "engineered for a load
|
||||
you are about to exceed", and the reasoning is baked on the root as `rated_for`.
|
||||
**AND 0.80 IS CHOSEN SO THAT IT USUALLY HOLDS. That is the tier, not a softness in it.** A corner
|
||||
that lets go one night in five costs you a shackle on a sail post and **$320** here. The player
|
||||
who learned "0.80 is fine" on every other yard is exactly the player this yard is for. The lesson
|
||||
is not "don't trust this steel" — the steel is fine. It is **"the same failure has a different
|
||||
price depending on what is underneath it"**, which is the sentence line 179 has been waiting for
|
||||
an asset to say.
|
||||
**COLLATERAL: the frame is UNPRICED, BY RULING** (the bike/jacaranda pattern). Its anchors bill
|
||||
the **glass** at $320 (unchanged, A's number, endorsed). A folded portal frame is obviously also
|
||||
a repair bill, but one broken corner can name exactly one collateral key, and inventing a second
|
||||
bill with no mechanism to split it is the guess the invoice exists to refuse. Price it when a
|
||||
break can bill two things.
|
||||
**MANIFEST — UNAMBIGUOUS.** `anchor_ratings.gen.js` regenerated: `hail_net_frame` → rating 0.80,
|
||||
`collateral: "glasshouse"`, **four agreeing sources**, not in `ambiguous`. So a yard can place
|
||||
this **node-less** (`factoryExtras` carries both the 0.80 AND the 'glasshouse' key off the type)
|
||||
exactly like the corroded post — D does not have to name nodes to author a glasshouse yard.
|
||||
It is the second type whose collateral names a **different** structure than itself; `house` →
|
||||
`"gutter"` is the precedent, and the shape is the same: **an anchor names what its failure TAKES,
|
||||
not what it is.**
|
||||
|
||||
**NEGATIVE CONTROLS — five, each witnessed RED in the real harness at :8828, my own fronted tab,
|
||||
then green. Batched only where the mutations hit disjoint tests, so every red is attributable.**
|
||||
· **M4 — the anchors stop naming the glass (the PRE-SPRINT state).** Witnessed free, and it is
|
||||
the headline: on the run where the two GLBs were built but not yet in e.test's own ASSETS list,
|
||||
the apex pin went red with exactly *"NOTHING in the palette can bill "glasshouse" ($320)"*.
|
||||
**That is the hole gate 4 was really about, printed by the test that closes it.**
|
||||
· **M1 — the frame stops bracketing the glass** (CLEAR_X/Y negative): BRACKETS red, *"the frame
|
||||
spans x −0.86..0.86 and the glasshouse −1.32..1.32"*. **Caught TWICE** — the factory's own dims
|
||||
band rejects the build outright, so this control needed `--no-verify` to even export. Belt and
|
||||
braces, and I'll take it.
|
||||
· **M2 — powder-coat becomes galvanised** (steel_gal at metallic 0.90): METALNESS red, *"the frame
|
||||
paints at metalness 0.8999…"*. The tell dying **silently** is the whole risk here: the frame
|
||||
stays white in the palette and goes black on screen.
|
||||
· **M3 — the frame CLAIMS the glass's key** (`collateral_key = "glasshouse"` on the root): apex
|
||||
pin red on its second half. This is gate 5's landmine **on my own asset**, which is why the
|
||||
omission is commented at the bake site rather than merely done.
|
||||
· **M5 / M6 — the gate-5 site controls**, below.
|
||||
Counts held at 533 through every control run (530 passed + 3 failed), so no test silently
|
||||
vanished while I was reddening others.
|
||||
**DETERMINISM — the rule I set in S17, held.** Three consecutive full factory runs; runs 2 and 3
|
||||
are **56/56 byte-identical** (39 GLBs, 4 verbatim debris copies, 8 textures, `asset_report.json`,
|
||||
`contact_sheet.png`, `anchor_ratings.gen.js`).
|
||||
|
||||
⚠️ **BLENDER VERSION IS PART OF THE OUTPUT — READ BEFORE YOU BUILD. This box is WRONG.**
|
||||
The shipped GLBs were exported by **Blender 5.1.2** (glTF I/O v5.1.20); `asset_report.json`
|
||||
records it in its `blender` field. This box has **5.0.1** (I/O v5.0.21). A full run here rewrote
|
||||
**all 45 binaries** — every GLB and every texture — and both differences are cosmetic:
|
||||
· GLB accessor min/max drift in the **7th decimal** (~6e−8 m, 60 nanometres). Node graph, mesh
|
||||
topology, materials and every custom prop are **bit-identical** (verified chunk by chunk).
|
||||
· The generated PNGs **lose their sRGB/gAMA/cHRM chunks** — exactly 73 bytes each. IDAT pixel
|
||||
data decompresses **bit-identically**; 5.0.x simply omits the colour-space chunks 5.1.x writes.
|
||||
**I reverted all 45 and committed only my two new files.** Shipping that churn would have been a
|
||||
45-binary diff nobody could review, hiding a real (if small) colour-management regression, to add
|
||||
two assets — my S17 words for this exact shape were *"a corrupt artifact, not a decision"*. It is
|
||||
now documented at the top of `build_yard_assets.py` so the next E session doesn't ship the
|
||||
downgrade by accident: if `git status` shows every GLB modified after you added one asset, that is
|
||||
this. **Better: build on 5.1.2 and the churn is zero.** I did NOT chase a second machine for it —
|
||||
adding a box to the asset pipeline to chase exporter metadata is an unforced variable, and the
|
||||
difference is provably semantically nil.
|
||||
|
||||
**THE WRECK — and I changed it because I LOOKED.** First cut racked the failed portal 0.30 m in
|
||||
and kicked its feet 0.16 m out. In the render the top frame **still read as a clean level
|
||||
rectangle**: a 0.43 m drop on a 2.77 m frame of 32 mm tube is invisible in perspective, and a
|
||||
wreck you cannot see is not a wreck. Now 0.45 each way — head IN (dragged by the sail), feet OUT
|
||||
— so the portal **racks into a parallelogram**, the carport's failure mode on a lighter section:
|
||||
~22° of lean, one foot clear of its pad, one **orphaned footing**, the top frame a visible
|
||||
trapezoid, and the net off its lashings draped down over the glass and heaped past the footing.
|
||||
Verified at 16 m in the game's renderer: unmistakably broken at a glance.
|
||||
The head beam rests **ACROSS the ridge** (2.33) rather than below it, deliberately: the
|
||||
glasshouse's own wreck keeps its aluminium frame standing at full height, so a beam that ended up
|
||||
under the ridge line would be *inside* it. **The wreck carries NO GLASS** (pinned): the glasshouse
|
||||
swaps independently, and shards in both would double-draw the night both swap — and would quietly
|
||||
claim the glass's failure as this asset's. **No anchor survives the wreck** (pinned; fascia rule,
|
||||
sixth application). Same height band as the intact frame, because a portal frame folds at one end
|
||||
— it does not lie down.
|
||||
|
||||
**PLACEMENT FACTS, three.js coords, measured off the exported GLBs (not off the builder):**
|
||||
· `hail_net_frame_01` — footprint **3.42 × 3.02 m**, height **2.766 m**. Four eyes,
|
||||
`net_anchor_01..04`, all at **y = 2.690**, on the corners of a 3.20 × 2.80 m rectangle:
|
||||
**(−1.600, 2.690, +1.400) · (+1.600, 2.690, +1.400) · (+1.600, 2.690, −1.400) ·
|
||||
(−1.600, 2.690, −1.400)**. Sized around `glasshouse_01` (2.65 × 1.95 × 2.30): **place both at
|
||||
the same x/z and the frame straddles it correctly with no fiddling.**
|
||||
· `hail_net_frame_01_wrecked` — **WRECK APRON: reserve ~0.9 m of clear ground on −X** beyond the
|
||||
footing line (x reaches −2.59 against the intact −1.71; the **net heap** sets that bound, not
|
||||
the racked feet). Height 2.783 m. It is the far side from the standing portal.
|
||||
|
||||
**📋 A / INTEGRATOR — EDITOR PALETTE HUNK, APPLY VERBATIM** (editor.js is A's; leadFor precedent,
|
||||
eighth application). Insert next to the glasshouse. A's own palette test uses `PALETTE.find`, not
|
||||
a length assert, so this adds no rows to a.test:
|
||||
|
||||
```js
|
||||
{
|
||||
// SPRINT18 gate 4, E. The glasshouse tier's LIVE half — the frame you rig a
|
||||
// hail net FROM, straddling the glass you must never rig TO. DESIGN.md line
|
||||
// 179: "hail nets and mesh sails rigged over glass, where your own failure
|
||||
// is the worst debris of all." Place it at the same x/z as a glasshouse and
|
||||
// it straddles correctly; the eyes land at y=2.690, clear of the 2.28 ridge.
|
||||
//
|
||||
// 0.80, and it USUALLY HOLDS — that is the tier, not a softness in it. Every
|
||||
// other trap here punishes you with a bad number; this is good steel
|
||||
// (engineered portal, four footings, welded eyes) and the hazard is WHERE IT
|
||||
// STANDS. The corner failure that costs a shackle on a sail post costs $320.
|
||||
//
|
||||
// ⚠️ `_v1` is LOAD-BEARING and its absence is SILENT: adoptAnchor does
|
||||
// `rating_hint ?? 1`, so a typo does not fail — it rates these four eyes
|
||||
// 1.00, the best steel in the game, on the highest-collateral anchor there
|
||||
// is. (S14, D's cold pass.)
|
||||
// ⚠️ THIS ITEM WRITES NO collateralKey, ON PURPOSE. The anchors bill
|
||||
// 'glasshouse'; the GLASSHOUSE owns that key. structFor() returns the FIRST
|
||||
// structure matching a key, so a frame that claimed it would bill the panes
|
||||
// and then wreck the FRAME — both papers agreeing and both wrong.
|
||||
// ⚠️ PLACE IT OVER A GLASSHOUSE. With no glasshouse in the site,
|
||||
// collateralFor('glasshouse') returns null and these four corners are a FREE
|
||||
// failure — the gutter bug. e.test pins that per-yard and names the anchor.
|
||||
kind: 'structure', label: 'Hail-net frame (0.80 — over the glass, $320 under it)',
|
||||
model: 'hail_net_frame_01_v1',
|
||||
requires: ['hail_net_frame'],
|
||||
hint: 'four good eyes at 2.7 m, rated 0.80 — and everything they drop lands on the glasshouse',
|
||||
make: (id, x, z) => ({
|
||||
id, model: 'hail_net_frame_01_v1',
|
||||
wreckedModel: 'hail_net_frame_01_wrecked_v1',
|
||||
x, z, rotYDeg: 0, solid: true,
|
||||
anchors: [1, 2, 3, 4].map((i) => ({
|
||||
id: `${id}_a${i}`, node: `net_anchor_0${i}`, type: 'hail_net_frame', work: 'cloth',
|
||||
})),
|
||||
}),
|
||||
},
|
||||
```
|
||||
|
||||
⚠️ **A — ANCHOR_TYPE WIDENED** (contracts.js is yours; carport / swing_frame / pergola /
|
||||
corroded_post precedent, fifth application). I added `'hail_net_frame'` with its reasoning beside
|
||||
it, including why it must NOT be typed `post`: "post" promises a corner whose failure costs a
|
||||
shackle. Revert-and-tell-me standard; your own "the widened enum is CHECKED" test covers it and
|
||||
is green.
|
||||
|
||||
**🪟 A — THE TIER IS PLACEABLE, AND THE ONE-VARIABLE LAW IS YOURS TO ENFORCE.** Both GLBs are
|
||||
landed, deterministic and manifest-resolvable **node-less**, so a glasshouse yard can be authored
|
||||
now. **Per SPRINT18's own hard gate: this must NOT debut on the same night as the emergency
|
||||
callout**, however well line 29 pairs them — the glasshouse debuts as an ordinary
|
||||
rig-for-tonight job, and the flogging-sail-over-glass sentence is the payoff, a later week. I am
|
||||
supplying it and saying so; the ordering is yours.
|
||||
|
||||
[E] 2026-07-25 — 🔑 **GATE 5 HALF-LANDED: THE SECOND-RUST-POST LANDMINE — my half, and the exact
|
||||
wiring A needs. DISARMED AT AUTHOR TIME; the runtime fix is one `??` and it is A's.**
|
||||
D's filing, with D's measured receipt ($115 on the offer card vs $45 on the failure route). The
|
||||
mechanism, re-read at source and confirmed: `world.js:526` `structFor(key)` iterates
|
||||
`structures.values()` and **returns the first match**; `main.js:877` dedupes bills by key;
|
||||
`scorecard.js:355` `collateralExposure` dedupes by key again; `board.js:330` `exposureOf` counts
|
||||
per **STRUCTURE**. All four are individually correct and they disagree the moment two structures
|
||||
share a key. **The pool yard dodges it by shipping exactly one corroded post on purpose — that is
|
||||
a yard author remembering, which is not a defence.**
|
||||
**AND GATE 4 JUST MADE IT LIKELIER, which is why these two gates are one seam.** A glasshouse
|
||||
yard now carries a structure that **owns** the key and a frame whose anchors **point at** it. Two
|
||||
glasshouses with a frame each trips this on the first night. So my own asset carries the omission
|
||||
deliberately (no `collateral_key` on the frame root) with the reasoning commented at the bake.
|
||||
|
||||
**MY HALF — two pins in e.test, both witnessed red then green:**
|
||||
· **`no shipped yard gives two structures the same collateral key, and ids are unique`.** Resolves
|
||||
each structure's key exactly the way `structKey()` does (site JSON → baked GLB key → id as last
|
||||
resort), across all four shipped yards. **M5 control — I added a second corroded post to the
|
||||
pool yard** and it went red naming both. It also pins **duplicate structure ids**, which is a
|
||||
separate silent death I found while reading: `structures.set(st.id, …)` means a duplicate id
|
||||
**silently replaces** the first structure — it never renders, never bills, never wrecks, and
|
||||
nothing anywhere says a word. `validateSite` checks duplicate ANCHOR ids and not structure ids.
|
||||
· **`every collateral an authored anchor names is OWNED by exactly one thing in that yard`.** The
|
||||
runtime twin of RULE 2: RULE 2 proves a key is priced *somewhere in the palette*, this proves it
|
||||
is owned *in the yard that names it*. **M6 control — I placed the net frame in the pool yard,
|
||||
which has no glasshouse** → red: *"nf1_a1 will bill "glasshouse" on failure, and 0 things in
|
||||
this yard own that key"*. **Zero owners is the gutter bug reborn**, and it is now caught before
|
||||
a yard ships rather than by a player breaking a corner for free.
|
||||
|
||||
**🔧 A — THE WIRING, EXACT. Two hunks in world.js (yours) and one in validateSite (yours).**
|
||||
The reason a site cannot currently just give two posts different keys: `structKey()` already
|
||||
prefers `spec.collateralKey`, so the STRUCTURE half works today. The **anchor** half does not —
|
||||
`adoptAnchor` takes the key off the baked GLB node, identical for every instance, and
|
||||
`factoryExtras(type)` does the same for node-less anchors. So renaming the structures' keys makes
|
||||
it **worse, not better**: both anchors still say `'corroded_post'`, which now matches neither
|
||||
`'corroded_post_a'` nor `'corroded_post_b'`, `collateralFor` returns null, and both corners
|
||||
become **FREE**. Hence:
|
||||
1. **`world.js:541`** (structure anchor creation) — let the site name its own target, applied
|
||||
AFTER `factoryExtras` so it wins:
|
||||
```js
|
||||
anchors.push(Object.assign(
|
||||
makeStaticAnchor(a.id, a.type ?? 'post', new THREE.Vector3(st.x, heightAt(st.x, st.z) + 2.4, st.z)),
|
||||
{ work: a.work },
|
||||
a.node ? null : factoryExtras(a.type ?? 'post'),
|
||||
a.collateral ? { collateral: a.collateral } : null, // SPRINT18 gate 5
|
||||
));
|
||||
```
|
||||
2. **`world.js:690`** (`adoptAnchor`) — one `??`, so the bake is the DEFAULT and never clobbers an
|
||||
override:
|
||||
```js
|
||||
anchor.collateral = anchor.collateral ?? node.userData?.collateral ?? null;
|
||||
```
|
||||
Every shipped site is byte-unchanged by both hunks: with no override the chain is identical.
|
||||
3. **`validateSite`** — the loud author-time refusal, mirroring my pin, next to the existing
|
||||
duplicate-anchor-id check: reject two structures resolving to one collateral key, and reject
|
||||
duplicate structure ids.
|
||||
**WHAT THAT BUYS, PRECISELY.** A yard can then ship two corroded posts: `collateralKey`
|
||||
`corroded_post_a` / `_b` on the structures, and the matching `collateral` on each structure's
|
||||
anchors. `collateralFor` resolves each to its own structure (the VALUE lives on the site spec, so
|
||||
$45 each), the right post wrecks, and **the two harnesses agree**: `exposureOf` counts two
|
||||
structures = $90, `collateralExposure` sees two keys = $90. **D's $115-vs-$45 split closes to
|
||||
$115 vs $90** — and the residual **$25 is exactly the gnome**, which `collateralExposure` excludes
|
||||
because no anchor reaches it. That is not a bug, it is **your ruling to make**: does an unreachable
|
||||
priced prop belong in the offer card's exposure total? I have no vote; I am handing you a number
|
||||
with one variable left in it instead of two.
|
||||
**AND THE SAME WIRING IS WHAT THE GLASSHOUSE TIER WILL WANT** for two-glasshouse yards, which is
|
||||
why I did not invent a second mechanism for it.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user