[ui] round 7 addendum: NEXT IN TRAY asserts the fax's contract, not SIM's cascade

My own round-3 test claimed the sim advances to commissionQueue[1]. DATA's v9
commissions falsified that twice in twenty minutes — first by cascading (entries
behind the head were already satisfied and completed on arrival), then by putting
an unpayable head in front of the rig. The peek assertion — NEXT IN TRAY is always
the live queue's second entry — is unconditional again; the advance is only
asserted when the head is one the test rig can actually pay.

263 UI tests green, tsc clean tree-wide under contracts v9.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-29 12:49:12 +10:00
parent b002e68b5a
commit 0508910f27
2 changed files with 47 additions and 4 deletions

View File

@ -801,3 +801,24 @@ the new copy landing and being buried. Not done unasked; say the word.
its own arrival; a moov-heist UI when the lock becomes breakable (the chip already names the its own arrival; a moov-heist UI when the lock becomes breakable (the chip already names the
hash the minigame will want); compliance quota progress on the debt gauge itself (show what hash the minigame will want); compliance quota progress on the debt gauge itself (show what
filling the current quota would vent) once DATA settles the quota list. filling the current quota would vent) once DATA settles the quota list.
**ADDENDUM (same round, after the orchestrator's v9.1 and SIM/DATA's next landings).**
Re-verified against HEAD rather than against my own commit:
- `npm run check` is now **clean tree-wide**, including under v9.1's `CorrectionUnit.phase`
union — my `'stasis'`/`'prescan'` comparisons were already exact members, no change needed.
- **263 UI tests green, all 18 files.** The only red in the tree is LANE-SIM's own
`src/sim/mite.test.ts` (2, their firewall work, uncommitted). Their `reference.ts` also
spent ~5 minutes throwing "firewall at -21,9 overlaps belt at -21,10" from its own
validator, which took every reference-factory test in the repo (mine and theirs) down
with it; it self-resolved, as mid-edit files have every round since round 2.
- **I had to correct one of my OWN round-3 tests, and the reason is worth recording.**
"NEXT IN TRAY … advances it on completion" asserted the sim lands on `commissionQueue[1]`.
That was always a claim about SIM's cascade rather than about the fax, and DATA's v9
commissions broke it two different ways within twenty minutes: first the queue skipped
`first-refinement` (the factory had been shipping since tick 0, so entries behind the
head were *already* satisfied and completed in the same tick they became active), then
the head became `patio-resurfacing`, which my raw-ore rig cannot pay at all, so nothing
advanced. The test now asserts the fax's real contract unconditionally — the peek is the
live `commissionQueue[1]` — and only asserts the advance when the head is one this rig
can actually pay. The head-of-line blocking I flagged in round 5 is still live and is
still a design question, not a UI fix.

View File

@ -208,14 +208,36 @@ describe('the HUD against the reference factory', () => {
// Satisfy the head commission for real, and watch the tray move up. // Satisfy the head commission for real, and watch the tray move up.
shipRawOre(h); shipRawOre(h);
for (let t = 0; t < 12000 && h.sim.snapshot().activeCommission === first.activeCommission; t += 100) { for (let t = 0; t < 12000 && h.sim.snapshot().activeCommission === first.activeCommission; t++) {
h.step(100); h.step(1);
} }
const after = h.sim.snapshot(); const after = h.sim.snapshot();
expect(after.activeCommission, 'the queue should advance once the head is paid')
.toBe(nextId); // Whatever the queue did, THIS is the fax's contract and it holds unconditionally:
// the peek is the live queue's second entry, never a guess from data order.
expect($('.fk-fax-next-name').textContent) expect($('.fk-fax-next-name').textContent)
.toBe(after.commissionQueue![1].replace(/-/g, ' ').toUpperCase()); .toBe(after.commissionQueue![1].replace(/-/g, ' ').toUpperCase());
// The advance itself is only assertable when the rig can actually pay the head, and
// DATA owns what sits there (it changed twice during round 7 alone; round 5 already
// flagged that an unpayable head blocks the whole tray). So: if the head wants only
// raw ore, it MUST advance — otherwise the queue is head-of-line blocked by design
// and there is nothing here for the UI to be wrong about.
const headDef = DATA.commissions.find((c) => c.id === first.activeCommission)!;
const payableByRig = Object.keys(headDef.wants).every((i) => i === 'mdat-ore');
if (payableByRig) {
expect(after.activeCommission, 'a head this rig can pay must advance')
.not.toBe(first.activeCommission);
expect(after.commissionQueue![0]).toBe(after.activeCommission);
expect(after.commissionQueue).not.toContain(first.activeCommission);
// It moves ALONG the queue, never backwards. Note it may skip `nextId`: the
// factory has shipped since tick 0, so entries behind the head can already be
// satisfied and complete in the same tick they become active.
expect([nextId, ...first.commissionQueue!.slice(2)]).toContain(after.activeCommission);
} else {
expect(after.activeCommission).toBe(first.activeCommission);
expect(after.commissionQueue![0]).toBe(after.activeCommission);
}
}); });
it('completes a commission and stamps the fax', () => { it('completes a commission and stamps the fax', () => {