Lane B S17 gate 3: the corroded tier prices honestly in the card
The piece the 0.2/0.3/2 pass explicitly deferred. Pins E's corroded tier
through the card's own pricing surfaces, off E's real resolver (not a
hand-typed 0.55):
- STEEL half (rigging.selftest): factoryExtras('corroded_post') -> hint 0.55
+ collateral key; _effRating on a corroded corner is rating x 0.55, so the
same load a carabiner holds on a sound post is over a carabiner on a
corroded one. The rust costs a tier.
- COLLATERAL half (gardenfly.selftest, real dressed world): a node-less
corroded placement -> collateralFor = 'the corroded post', summed by
collateralExposure -- through the resolver, not the baked GLB value direct
(the carport-to-null shortcut).
Cross-lane seam: the tier lives on lane/e, so both pins skip-with-disclosure
on bare lane/b (SKIPPED, visible in the report) and go live at integration.
Measured GREEN on a scratch merge of lane/a + lane/e (hint 0.55, collateral
). Browser 485/3/0, node sail 47/47 rigging 29/29.
Test-only + THREADS; adopted the landed 0.2/0.3/2 unchanged rather than
overwrite pushed work.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
5befa54feb
commit
4cdb2fead8
46
THREADS.md
46
THREADS.md
@ -8353,3 +8353,49 @@ anchors are your GLB), but the tooling is now waiting, not TODO.
|
||||
|
||||
Pushed lane/b: aa0b36a (gate 0.2), 1bcfcda (gates 0.3+2), this entry. Port 8825, own tab
|
||||
throughout; the full suite green in the browser before every push.
|
||||
|
||||
[B] 2026-07-21 — 🦀 **GATE 3 LANDED: THE CORRODED TIER PRICES HONESTLY IN THE CARD — the piece the
|
||||
0.2/0.3/2 pass deferred, now pinned. Plus: I re-ran 0.2/0.3/2 independently and they STAND.**
|
||||
A recovered/parallel session (this one) rebuilt gates 0.2/0.3/2 from 75d5f9a before finding the
|
||||
landed work on origin/lane/b. Rather than overwrite three pushed, receipted, mutation-checked
|
||||
commits, I **reset to the landed versions and added only gate 3** — the one thing the earlier
|
||||
pass explicitly filed as DEFERRED ("gate 3 support is armed and DEFERRED as briefed… Corroded
|
||||
steel prices through ratingHint, which every sweep already reads live"). The parallel
|
||||
reimplementation is kept in a local ref for reference; nothing from it overwrote the landed
|
||||
lane/b. selftest still green (node sail 47/47, rigging 29/29 = 27 + the 2 gate-3 pins).
|
||||
|
||||
**What gate 3 pins**, off E's REAL resolver, not a hand-typed 0.55:
|
||||
· **The STEEL half** (`rigging.selftest`): `factoryExtras('corroded_post')` → hint 0.55 +
|
||||
collateral key, and `_effRating` on a corroded corner is `rating × 0.55` — so a load a
|
||||
carabiner holds on a sound post (1200 N) is OVER a carabiner on a corroded one (660 N). The
|
||||
rust costs a tier, measured, and if it EVER prices as sound steel the trap inverts into the
|
||||
safest anchor in the yard (E's load-bearing `_v1` warning).
|
||||
· **The COLLATERAL half** (`gardenfly.selftest`, a real dressed world, ~40 ms): a node-less
|
||||
corroded placement (E's "plain entry, no node field" case — the harder one, rating and
|
||||
collateral both from the factory) → `world.collateralFor('corroded_post')` = $45 "the
|
||||
corroded post", summed by `collateralExposure` — NOT the baked GLB value read directly, the
|
||||
shortcut that priced the carport to null for five sprints.
|
||||
**Mutations** (run on the scratch merge where the tier is live): `collateralFor` drops the
|
||||
price → card reads null; `factoryExtras` returns hint 1 → the steel pin reddens. Both caught.
|
||||
|
||||
⚠️ **CROSS-LANE SEAM, disclosed.** The corroded tier (0.55, the GLB, the gen entry) is on
|
||||
lane/e. On bare lane/b `factoryExtras('corroded_post')` is null, so **both gate-3 pins
|
||||
SKIP-WITH-DISCLOSURE here** and go live at integration — the "build against the shape as
|
||||
pushed" pattern. Measured GREEN on a scratch merge of lane/a + lane/e (hint 0.55, collateral
|
||||
$45). Browser suite on bare lane/b: 0 failures, the corroded pins visibly skipped with their
|
||||
reason in the run.
|
||||
|
||||
**D — your pool yard's corroded corners price right the moment you place them**, node or
|
||||
node-less; the card names the $45 collateral and the 0.55 steel with no round-trip to me. The
|
||||
tier is E's, the pricing surfaces are pinned, and the pin activates itself when your yard and
|
||||
E's tier meet on the integrated tree.
|
||||
|
||||
**On 0.2/0.3/2 (the landed pass): reviewed, sound, nothing to change.** One optional
|
||||
strengthening I found while reimplementing, filed NOT forced — the 0.2 negative control's
|
||||
"over-arming is EQUIVALENT" finding is real (the heal is pure repair and emits only when it
|
||||
healed, so an over-arm is a silent no-op to an event-counter). It IS observable if the control
|
||||
spies the heal CALL rather than the event: a stub that arms on finite state calls
|
||||
`_healNonFinite` every substep of a fully-broken-but-finite sail. If a future pass wants to
|
||||
close that mutation gap, a `let calls=0; wrap _healNonFinite` in the finite-lost-sail control
|
||||
turns the recorded "equivalent" into a red. Not this landing; the current control is honest
|
||||
about what it does and does not catch.
|
||||
|
||||
@ -33,6 +33,15 @@ import { createSkyFx } from '../../web/world/js/skyfx.js';
|
||||
import { createGarden } from '../../web/world/js/garden.js';
|
||||
import { FIXED_DT } from '../../web/world/js/contracts.js';
|
||||
import { flyGarden, flySeparation, hardwareByName } from './gardenfly.js';
|
||||
import { buildScoringWorld, collateralExposure } from './scorecard.js';
|
||||
import { factoryExtras } from '../../web/world/js/ratings.js';
|
||||
|
||||
// SPRINT17 gate 3 [B] — CROSS-LANE SEAM. The corroded tier (rating_hint 0.55,
|
||||
// the GLB, the gen entry) lives on lane/e. On a bare lane/b it is absent, so
|
||||
// this pin SKIPS WITH A DISCLOSURE and goes live at integration — the same
|
||||
// "build against the shape as pushed" seam the sprint runs on. Measured GREEN
|
||||
// on the scratch merge (lane/a + lane/e): hint 0.55, collateral $45.
|
||||
const CORRODED_TIER_LIVE = !!(factoryExtras('corroded_post'));
|
||||
|
||||
const assert = (cond, msg) => { if (!cond) throw new Error(msg); };
|
||||
|
||||
@ -151,7 +160,62 @@ export async function buildGardenflyTests() {
|
||||
fabRun = { byDefault: flyFab(undefined), cloth: flyFab(0.30), membrane: flyFab(0) };
|
||||
} catch (err) { fabErr = String((err && err.stack) || err); }
|
||||
|
||||
// SPRINT17 gate 3 support [B]: the corroded tier priced through the CARD'S
|
||||
// OWN path, in a real dressed world. E landed the post; the 0.2/0.3/2 pass
|
||||
// deferred gate 3; this pins that a corroded anchor prices honestly the
|
||||
// moment it is placed. Node-less placement (E's THREADS: "place with a plain
|
||||
// entry and no node field and the rating still lands") — the harder case,
|
||||
// where the rating and the collateral both come from the factory, not a
|
||||
// dress. buildScoringWorld only DRESSES (no sweep), so this is ~40 ms.
|
||||
let corRun = null, corErr = null;
|
||||
try {
|
||||
if (!CORRODED_TIER_LIVE) throw { skip: true };
|
||||
const corSite = await loadSite('backyard_01');
|
||||
corSite.structures = [...(corSite.structures ?? []), {
|
||||
id: 'cor1', model: 'sail_post_corroded_v1', wreckedModel: 'sail_post_corroded_wrecked_v1',
|
||||
x: 6, z: 2, rotYDeg: 0, solid: true,
|
||||
collateralKey: 'corroded_post', collateralValue: 45, collateralLabel: 'the corroded post',
|
||||
anchors: [{ id: 'cor1_a1', type: 'corroded_post', work: 'cloth' }],
|
||||
}];
|
||||
const built = await buildScoringWorld(corSite);
|
||||
const anc = built.anchors.find((a) => a.id === 'cor1_a1');
|
||||
const col = {};
|
||||
for (const a of built.anchors) {
|
||||
if (!a.collateral) continue;
|
||||
const p = built.world.collateralFor(a.collateral);
|
||||
col[a.id] = p ? { key: a.collateral, cost: p.cost, label: p.label }
|
||||
: { key: a.collateral, cost: null, label: a.collateral, unpriced: true };
|
||||
}
|
||||
corRun = { anchor: anc, priced: built.world.collateralFor('corroded_post'),
|
||||
exposure: collateralExposure({ collateral: col }), dressed: built.dressed, dressError: built.dressError };
|
||||
} catch (err) { if (err && err.skip) corErr = 'skip'; else corErr = String((err && err.stack) || err); }
|
||||
|
||||
return [
|
||||
['gate 3: a corroded post prices honestly in the card — 0.55 hint, $45 collateral, dressed', () => {
|
||||
if (!CORRODED_TIER_LIVE) return 'SKIPPED — corroded tier lands with lane/e; measured GREEN on '
|
||||
+ 'the scratch merge (hint 0.55, collateral $45 through collateralFor)';
|
||||
if (corErr) throw new Error(`corroded pricing died: ${corErr}`);
|
||||
assert(corRun.dressed, `the corroded world did not dress (${corRun.dressError}) — GLB missing or broke`);
|
||||
const a = corRun.anchor;
|
||||
assert(a, 'the corroded anchor cor1_a1 was not created');
|
||||
// The STEEL half, node-less: factoryExtras had to reach it, or the tier
|
||||
// math prices this trap as the best steel in the game (adoptAnchor's
|
||||
// `?? 1`, E's load-bearing _v1 warning).
|
||||
assert(a.ratingHint === 0.55, `corroded anchor rated ${a.ratingHint}, not 0.55 — a node-less placement `
|
||||
+ 'lost E\'s factory hint and the card would price it as sound steel');
|
||||
assert(a.collateral === 'corroded_post', `corroded anchor collateral "${a.collateral}", not "corroded_post"`);
|
||||
// The COLLATERAL half, through the card's OWN resolver and exposure sum —
|
||||
// not the baked GLB value read directly (the carport-to-null shortcut).
|
||||
assert(corRun.priced && corRun.priced.cost === 45,
|
||||
`world.collateralFor('corroded_post') gave ${JSON.stringify(corRun.priced)}, expected $45`);
|
||||
assert(corRun.priced.label === 'the corroded post', `collateral label "${corRun.priced.label}"`);
|
||||
const line = corRun.exposure.priced.find((e) => e.key === 'corroded_post');
|
||||
assert(line && line.cost === 45,
|
||||
`collateralExposure has no $45 corroded line (${JSON.stringify(corRun.exposure.priced.map((e) => [e.key, e.cost]))})`);
|
||||
assert(!corRun.exposure.unpriced.some((e) => e.key === 'corroded_post'),
|
||||
'the corroded post landed in UNPRICED — the exposure total silently drops its $45');
|
||||
return `corroded_post: hint ${a.ratingHint}, collateral $${corRun.priced.cost} "${corRun.priced.label}", in the exposure sum`;
|
||||
}],
|
||||
['gardenfly: the pinned separation line, flown in BOTH chains — the 12s-skew disagreement, pinned', () => {
|
||||
// ⚠ THE STATE OF PLAY (2026-07-18, [B] THREADS, receipts in the entry):
|
||||
// the pinned recipe HOLDS and rigging matters by a full state's width —
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import { RiggingSession, FABRIC, fabricNoteFor } from './rigging.js';
|
||||
import { SailRig, TENSION_MIN, TENSION_MAX } from './sail.js';
|
||||
import { HARDWARE, START_BUDGET, SPARE_COST } from './contracts.js';
|
||||
import { factoryExtras } from './ratings.js';
|
||||
|
||||
const [CARABINER, SHACKLE, RATED] = HARDWARE;
|
||||
|
||||
@ -494,6 +495,66 @@ test('gate 2: the constraint door is a checked enum, and the terms survive reset
|
||||
return 'five malformed shapes thrown out at the door; terms survive reset, cleared only explicitly';
|
||||
});
|
||||
|
||||
// --- SPRINT17 gate 3 support: THE CORRODED TIER PRICES HONESTLY -------------
|
||||
// E landed the corroded post (rating_hint 0.55, its own ANCHOR_TYPE word, a
|
||||
// $45 collateral); the gates-0.2/0.3/2 pass DEFERRED gate 3, and this is it.
|
||||
// B owns the pricing surfaces the card reads, so the number the shop charges
|
||||
// for a corroded corner is pinned HERE, off E's real resolver — not a
|
||||
// hand-typed 0.55. The collateral half ($45 through world.collateralFor → the
|
||||
// exposure line) is pinned in gardenfly.selftest, where a dressed world exists;
|
||||
// this is the STEEL half: a corroded post is weaker, so the same load costs
|
||||
// MORE steel to hold, which is what "prices honestly" means and is B's tier
|
||||
// math (_effRating), not E's mesh.
|
||||
//
|
||||
// ⚠️ CROSS-LANE SEAM. The corroded tier lives on lane/e; on lane/b in
|
||||
// isolation `factoryExtras('corroded_post')` is null (no gen entry, no GLB).
|
||||
// These pins SKIP WITH A DISCLOSURE on a bare lane/b and go live the moment
|
||||
// lane/e merges — the "build against the shape as pushed" seam the sprint runs
|
||||
// on. Measured GREEN on the scratch merge (lane/a + lane/e): hint 0.55.
|
||||
const CORRODED_TIER_LIVE = !!(factoryExtras('corroded_post'));
|
||||
const CORRODED_SKIP = 'SKIPPED — corroded tier is on lane/e, absent on bare lane/b; this pin is '
|
||||
+ 'inert here and goes live at integration (measured GREEN on the scratch merge, hint 0.55)';
|
||||
|
||||
test('gate 3: the corroded tier is 0.55, read off E\'s resolver not a literal', () => {
|
||||
if (!CORRODED_TIER_LIVE) return CORRODED_SKIP;
|
||||
const ex = factoryExtras('corroded_post');
|
||||
assert(ex && ex.ratingHint === 0.55,
|
||||
`factoryExtras('corroded_post') gave ${JSON.stringify(ex)} — the card reads this for a node-less `
|
||||
+ 'corroded placement, and if E re-rules the number it must move here, not be re-typed');
|
||||
assert(ex.collateral === 'corroded_post',
|
||||
'the corroded anchor carries no collateral key — the exposure line would read "not scored" over a $45 trap');
|
||||
return `corroded_post → hint ${ex.ratingHint}, collateral "${ex.collateral}"`;
|
||||
});
|
||||
|
||||
test('gate 3: a corroded corner costs MORE steel than a sound post at the same load', () => {
|
||||
if (!CORRODED_TIER_LIVE) return CORRODED_SKIP;
|
||||
// _effRating is what the panel's arrow and the audit's tier both price
|
||||
// against: hardware.rating × the anchor's ratingHint. A corroded post fails
|
||||
// at 0.55 of the steel's spec, so a load a carabiner holds on a sound post
|
||||
// may need a shackle on a corroded one. Built off E's real hint.
|
||||
const hint = factoryExtras('corroded_post').ratingHint;
|
||||
const SOUND = { id: 'ps', type: 'post', pos: { x: -6.4, y: 3.9, z: 7.4 }, sway: () => ({ x: -6.4, y: 3.9, z: 7.4 }) };
|
||||
const COR = { id: 'pc', type: 'corroded_post', pos: { x: 5.3, y: 3.9, z: 8 }, ratingHint: hint,
|
||||
sway: () => ({ x: 5.3, y: 3.9, z: 8 }) };
|
||||
const s = new RiggingSession({ anchors: [SOUND, COR, ...ANCHORS] });
|
||||
s.rig('ps'); s.rig('pc'); s.rig('h1'); s.rig('t1');
|
||||
const sound = s._effRating(s.pickOf('ps'));
|
||||
const cor = s._effRating(s.pickOf('pc'));
|
||||
// Both start on carabiners (rating 1200): the sound post holds 1200 N, the
|
||||
// corroded holds 1200 × 0.55 = 660 N. Same steel, less strength — measured.
|
||||
assert(sound === CARABINER.rating, `sound post effRating ${sound}, expected the carabiner's ${CARABINER.rating}`);
|
||||
assert(Math.abs(cor - CARABINER.rating * hint) < 1e-9,
|
||||
`corroded effRating ${cor}, expected ${CARABINER.rating} × ${hint} = ${CARABINER.rating * hint}`);
|
||||
assert(cor < sound,
|
||||
'a corroded post held as much as a sound one — the trap is priced as safe steel, which inverts it');
|
||||
// The tier consequence: a load in the corroded gap (660–1200 N) is HELD by a
|
||||
// carabiner on the sound post and OVER a carabiner on the corroded one.
|
||||
const load = 900;
|
||||
assert(load < sound && load > cor,
|
||||
`pick a load in the corroded gap (${cor}..${sound} N) so the tier difference is real`);
|
||||
return `same carabiner: sound holds ${sound} N, corroded holds ${cor} N — the rust costs a tier`;
|
||||
});
|
||||
|
||||
export const RIGGING_TESTS = TESTS;
|
||||
|
||||
export function runRiggingSelftest() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user