Lane E S15: THREADS — gate 1.2 receipts, the exact world.js patch for the integrator (leadFor precedent), ruling constraint satisfied by construction, palette proposals for the next yard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2a540523ff
commit
cebc14e2b1
134
THREADS.md
134
THREADS.md
@ -6903,3 +6903,137 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
|
|||||||
|
|
||||||
Not touched, per the sprint's own sequencing: gate 1.2/1.3/1.4 (E → B → D, in that order,
|
Not touched, per the sprint's own sequencing: gate 1.2/1.3/1.4 (E → B → D, in that order,
|
||||||
starting from the ruling above) and gate 2 (B's iterative score, D judging).
|
starting from the ruling above) and gate 2 (B's iterative score, D judging).
|
||||||
|
|
||||||
|
[E] 2026-07-20 — 🔩 **GATE 1.2 LANDED: THE FACTORY AGREES WITH CANON, AND A BAKED HINT CAN
|
||||||
|
NEVER SILENTLY MISS A JSON ANCHOR AGAIN.** Selftest **412/0/0** (410 baseline + 2, both
|
||||||
|
mutation-checked red-then-green in the browser harness). My half is on lane/e; the
|
||||||
|
world.js half is a pre-agreed patch for the integrator, below — the leadFor precedent,
|
||||||
|
exactly one wire crossing the lane boundary at merge.
|
||||||
|
|
||||||
|
**1 · THE RE-BAKE (the ruling, taken).** `sail_post.top_anchor` now bakes
|
||||||
|
`rating_hint 1.0`, with the ruling quoted in the factory comment so nobody "fixes" it
|
||||||
|
back to 0.90 without re-opening gate 1.1. Full factory run, and the receipts:
|
||||||
|
· **only `sail_post_v1.glb` changed** out of 30 regenerated GLBs — same byte size
|
||||||
|
(36192), only the extras float moved. Confirmed in the binary: the glTF JSON chunk's
|
||||||
|
`top_anchor` node reads `{"anchor_type": "post", "rating_hint": 1.0}`.
|
||||||
|
· **Determinism, measured not assumed:** two full factory runs back to back,
|
||||||
|
`shasum -c` over every GLB + the report + the new manifest — all identical. Even
|
||||||
|
`contact_sheet.png` re-rendered byte-identical.
|
||||||
|
· Render checked: sail_post thumb still a 4.03 m post beside the 1.7 m capsule, 528 tris.
|
||||||
|
|
||||||
|
**2 · THE MECHANISM (the actual bug, buried).** The bug was never the number — it was
|
||||||
|
that `adoptAnchor` is the ONLY wire from a baked hint to a live anchor, and it only runs
|
||||||
|
for anchors that name a GLB `node`. Every shipped post is a plain JSON entry with no
|
||||||
|
node, so the factory said 0.90, `?? 1` said 1.00, and the sim won by accident for
|
||||||
|
fourteen sprints (D measured this from the editor's SELECTED panel; the fresh-eyes
|
||||||
|
review named the disease). The fix is a second wire, typed not name'd, and it covers the
|
||||||
|
whole extras family (`rating_hint`, `collateral`, and by exclusion `tie_off`):
|
||||||
|
· **`web/world/js/anchor_ratings.gen.js` (GENERATED, mine)** — the factory now extracts
|
||||||
|
every anchor-family extra off the RE-IMPORTED GLBs at verify time (so it describes
|
||||||
|
what shipped, not what the script intended) and emits a per-TYPE manifest. A type
|
||||||
|
where every rated node in the palette agrees on (rating_hint, collateral) is
|
||||||
|
unambiguous and resolvable: `post 1.0`, `house 0.35/gutter`, `carport 0.22/carport`,
|
||||||
|
`carport_post 0.30/carport`, `swing_frame 0.45/swing_set`. A type whose nodes
|
||||||
|
disagree (`tree`: 1.0 → 0.4 down six rungs) lands under `ambiguous` with every
|
||||||
|
variant listed, and the runtime REFUSES to guess for it. `--only` runs leave the
|
||||||
|
manifest untouched, loudly — a partial palette could flip a type's ambiguity.
|
||||||
|
`tools/blender/asset_report.json` also carries the per-asset `anchors` blocks now,
|
||||||
|
as receipts.
|
||||||
|
· **`web/world/js/ratings.js` (hand-written, mine)** — `factoryExtras(type)` returns
|
||||||
|
`{ratingHint, collateral?}` for unambiguous types, null otherwise, and warns once per
|
||||||
|
type if a node-less anchor asks for an ambiguous one (that's a site bug: declare a
|
||||||
|
node). RULE 1 got a factory-side tooth too: a node typed but unrated and undenied now
|
||||||
|
fails the BUILD, not just the test.
|
||||||
|
· A silent divergence now requires beating three pinned agreements at once:
|
||||||
|
GLB === manifest (RULE 4a), runtime === manifest (RULE 4b), and the factory's own
|
||||||
|
verify. Hand-edit the gen file, re-bake without regenerating, or regrow the
|
||||||
|
adoptAnchor gap — each goes red on its own assert.
|
||||||
|
|
||||||
|
**3 · THE WORLD.JS PATCH — integrator, this is yours to apply at merge, verbatim.
|
||||||
|
Four hunks, all the same shape: a node-less anchor takes the factory's word at
|
||||||
|
construction, because dress() will never reach it; a node-carrying anchor keeps its
|
||||||
|
graybox default and is corrected by adoptAnchor exactly as before.**
|
||||||
|
|
||||||
|
Hunk 1 — imports (after the contracts.js import, ~line 15):
|
||||||
|
```js
|
||||||
|
import { factoryExtras } from './ratings.js';
|
||||||
|
```
|
||||||
|
Hunk 2 — house anchors (~line 350):
|
||||||
|
```js
|
||||||
|
anchors.push(Object.assign(
|
||||||
|
makeStaticAnchor(a.id, a.type ?? 'house', new THREE.Vector3(-5 + i * 5, 2.6, -9.9)),
|
||||||
|
{ work: a.work },
|
||||||
|
a.node ? null : factoryExtras(a.type ?? 'house'),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
Hunk 3 — posts (~line 464):
|
||||||
|
```js
|
||||||
|
anchors.push(Object.assign(
|
||||||
|
makeStaticAnchor(spec.id, spec.type ?? 'post', top),
|
||||||
|
{ work: spec.work },
|
||||||
|
spec.node ? null : factoryExtras(spec.type ?? 'post'),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
Hunk 4 — structure anchors (~line 533):
|
||||||
|
```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'),
|
||||||
|
));
|
||||||
|
```
|
||||||
|
Tree anchors need no hunk: they always declare nodes in every shipped site, and `tree`
|
||||||
|
is ambiguous so `factoryExtras` would return null anyway — but if A prefers symmetry,
|
||||||
|
the same one-liner on the makeSwayAnchor push is a no-op and I have no objection.
|
||||||
|
`Object.assign` ignores null, so every hunk is inert exactly where adoptAnchor already
|
||||||
|
owns the job. With the ruling at 1.00 these hunks change ZERO shipped numbers —
|
||||||
|
`factoryExtras('post')` returns `{ratingHint: 1}`, which is what `DEFAULT_RATING_HINT`
|
||||||
|
was already saying. The patch exists for the NEXT asset, not this one.
|
||||||
|
|
||||||
|
**4 · THE PIN — RULE 4, the third of the family** (no silent anchors, no unpriced
|
||||||
|
collateral, and now no baked hint that never arrives), in e.test.js, compared against
|
||||||
|
the MANIFEST and never against a literal — a pin that hard-coded 1.0 would have been
|
||||||
|
green through the entire bug:
|
||||||
|
· **RULE 4a** — manifest === GLBs, both directions: every source the manifest cites
|
||||||
|
exists and carries the same bake; every rated, non-denying anchor node in the palette
|
||||||
|
is accounted for as unambiguous or ambiguous. Vacuity-guarded (an empty manifest is a
|
||||||
|
fail, not a pass).
|
||||||
|
· **RULE 4b** — runtime === manifest: loads all three shipped sites, builds the real
|
||||||
|
worlds, and for every JSON-declared node-less anchor of a factory-rated type asserts
|
||||||
|
`anchor.ratingHint === manifest.rating_hint` and collateral agreement; refuses a
|
||||||
|
node-less anchor of an ambiguous type outright. Vacuity-guarded (backyard_01's five
|
||||||
|
posts guarantee it checks something; lose them all and it goes red).
|
||||||
|
· **Mutation check, the one the gate demanded:** set the manifest's post entry to 0.90
|
||||||
|
— the shape of this exact bug — and BOTH pins went red in the same run:
|
||||||
|
RULE 4a: `sail_post/top_anchor bakes rating_hint 1 but the manifest says 0.9`.
|
||||||
|
RULE 4b: `backyard_01/p1 plays at ratingHint 1; the factory baked 0.9 — the baked
|
||||||
|
hint never arrived`. That second message is word-for-word the sentence nobody could
|
||||||
|
say for fourteen sprints. Restored (hash-verified against the factory's own output),
|
||||||
|
green: 412/0/0.
|
||||||
|
|
||||||
|
**5 · THE RULING CONSTRAINT IS SATISFIED BY CONSTRUCTION.** The re-bake (this branch)
|
||||||
|
and the mechanism's world.js half (the patch above) land in the SAME integration —
|
||||||
|
there is no commit anywhere at which a post reads 0.90, because the re-baked GLB says
|
||||||
|
1.00, the manifest says 1.00, and until the patch is applied `?? 1` says 1.00 too.
|
||||||
|
B: your gate-1.3 re-measure should find ZERO deltas — that's the cheap verification the
|
||||||
|
ruling promised you, and if you find a nonzero one, the mechanism is the suspect, not
|
||||||
|
the tuning.
|
||||||
|
|
||||||
|
**6 · THE S14 OFFER, kept — what the next yard's palette needs, read out of D's
|
||||||
|
cold-authoring entry. Proposals only; A rules, numbers wear their reasoning:**
|
||||||
|
· **An honest middle rung between 0.45 and 1.00.** D authored site_03 by reading the
|
||||||
|
palette and the ladder jumps from the swing frame (0.45, on grass) straight to the
|
||||||
|
concreted post (1.00). A `pergola_01` at **rating_hint 0.65, collateral "pergola"
|
||||||
|
~$120** fills it: bolted to a deck, real bolts, no footing — better than a welded
|
||||||
|
frame on grass, worse than 300 mm of concrete, and the number argues exactly that.
|
||||||
|
One type, one rating, so it stays manifest-unambiguous and node-less-safe.
|
||||||
|
· **New types should ship ONE rating per type.** The manifest just made type-level
|
||||||
|
ambiguity a visible property: `tree` is ambiguous (the ladder IS the feature — six
|
||||||
|
rungs from 1.0 to 0.4, resolved per-node), and that's correct FOR TREES because tree
|
||||||
|
anchors always name nodes. A structural type that varies per-node forces every future
|
||||||
|
site to know node names; a type with one honest number works declared bare. Cheap
|
||||||
|
rule, real freedom preserved: it's a default, not a law — but breaking it should
|
||||||
|
cost a sentence of reasoning in the factory comment, like this one.
|
||||||
|
|
||||||
|
Pushed to origin lane/e: the factory re-bake + manifest machinery, the regenerated
|
||||||
|
GLB + report + gen module, ratings.js, the two pins, and this entry.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user