Files
stack/agents/filbert/work/queue-33/build.md
T
jason.woltjeandClaude Opus 5.5 6fb50cc0a8 fix(queue): genesis owner-not-reviewer check, assign wording, queue-commit HEAD-moved message, calendar dates (row 33, #1508)
Built by filbert, approved by darkwing in round 1 (#1508 comment 26651).
Manifest fe7da3ef, 10 paths. Suites green on an index export.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-04 01:25:06 -05:00

6.4 KiB

Queue row 33 build notes (#1508)

Filbert, 2026-10-04. Brief: docs/plans/2026-10-04_queue-follow-ups.md (blob 1dc08bf0…). Reviewer: Darkwing. Base: HEAD 3e39a26a, queue rev 45. Candidate: candidate-manifest.sha256 in this directory, uncommitted. Sage commits it after approval.

What changed

  1. Genesis and the owner-not-reviewer rule. parseMigrationMap refuses a map row whose owner is among its reviewers: "migration map row N owner SEAT can't be a listed reviewer", exit 2. Genesis parses the map before it writes anything, so no file, witness or view changes.
  2. Assign wording. "can't assign row N to SEAT: SEAT is one of its reviewers". The two tests that matched the old text now match this one.
  3. HEAD moved. queue-commit.sh gains head_moved, called in guard_check before the canary and again when the clean run fails. A moved HEAD exits 2 with "refused (STEP): HEAD moved since H was recorded (another commit landed); run queue-commit.sh again". A real guard failure, with HEAD unmoved, keeps the old message.
  4. Calendar dates. checkTime refuses any ISO time or date that doesn't format back to itself through Date.parse and toISOString: "WHAT is not a calendar time|date: VALUE". A shape error keeps its old message. Every time the validator reads goes through checkTime: row times, review times and log entry times.
  5. Cold start. See "Cold runs" below.

README: the commit steps, the owner rule and the time format. DEFERRED: four items move to Done, and the cold-start item too, if no failure shows up.

Choices

  • C1. The genesis check sits in the map parser, not in replay. The review-record refusal is kept as defense in depth for a log written before the rule (review.test.mjs). A check in checkGenesis would make such a log unreadable. A new data test pins this: the parser refuses the map, and a genesis document built past the parser still loads. Mutant M10 (the rule added to replay) is killed by it.
  • C2. "another commit landed", not "another queue commit landed". The brief quotes the second wording. The check before the canary fires for any commit, including one that touches no queue file, so "queue" would be wrong there. The rest of the message is the brief's.
  • C3. HEAD is checked twice. The brief asks for a check before the canary. A commit can still land between that check and the hook run, and then the old misdiagnosis comes back. A second check, only when the clean run fails, closes that gap. It adds one rev-parse on a failing path only.
  • C4. Two existing F1 tests changed.
    • "HEAD moving after commit-tree and before update-ref" moved its trigger to before update-ref. A move after commit-tree is now caught at the step-7 check (exit 2). The test still proves update-ref's expected-old-value check, now at the only point where it's the last line.
    • "H is recorded before the canary…": a commit outside the queue files during the step-1 canary used to reach update-ref (exit 1). It's now refused at the step-7 check (exit 2), before that check's canary, and update-ref never runs. The test asserts that.
  • C5. 24:00 is refused. V8 parses T24:00:00.000Z as the next day's midnight, so it doesn't round-trip. Minute 60 and second 60 already parse as NaN.

Tests

  • data.test.mjs:
    • a new genesis test (map refusal; replay tolerance);
    • a new calendar test: month 13, month 0, day 32, 2026-02-30, 2027-02-29 and 2026-04-31 as dates in requiredSince and createdAt; the same days plus 24:00, minute 60 and second 60 as ISO times in updatedAt and requiredSince; 2028-02-29 valid in both forms; the shape message kept; a log entry at refused on replay;
    • the assign wording.
  • store.test.mjs: genesis from a map with row 9's owner among its reviewers exits 2. For that repository and the two refusals before it, there's no queue.json, no witness and an unchanged QUEUE.md. Also the assign wording.
  • commit.test.mjs, two new tests, both with a nested queue-commit.sh run landing a real queue commit:
    • after symbolic-ref (H recorded, before step 1's check): refused at step 1 with the HEAD-moved message, no canary run, and a rerun commits the next revision;
    • before the first git hook run: the clean run fails on the moved HEAD, and the recheck reports HEAD moved, not the guard. The existing "the canary refuses a hook that git would not run" test still covers the guard message.

Mutants

Eleven, run on an export of the candidate. Ten are killed:

Mutant Killed by
M1 no HEAD check before the canary step-1 and step-7 HEAD tests
M2 no recheck after a failed clean run the between-check-and-canary test
M3 head_moved exits 1 all three HEAD tests
M4 no round trip calendar test
M5 NaN check only calendar test
M6 round trip on ISO times only calendar test
M7 round trip on dates only calendar test
M9 genesis rule removed data and store genesis tests
M10 genesis rule also in replay data genesis test
M11 old assign wording data and store assign tests

One survives:

  • M8 compares only the date part of an ISO time. It's equivalent under V8. The only out-of-range time V8 parses is 24:00, and that moves the date, so the date part catches it.

Suites

On the final candidate bytes, HEAD 3e39a26a:

  • canonical checkout: node --test packages/queue/tests/ passes 148/148 and bash scripts/test-queue.sh 29/0;
  • an export of HEAD with the candidate files: bash scripts/test-queue.sh 27/0 (the live checks skip, as designed);
  • queue verify --current: ok at rev 45. The live log replays under the calendar check.

Cold runs

Not reproduced in 20 cold runs. cold.sh (here) built each tree fresh from HEAD 3e39a26a plus the candidate files: odd runs as a git clone --shared with push set to DISABLED, even runs as a git archive export. It ran node --test packages/queue/tests/ once in each, one run at a time with nothing else running, then deleted the tree. Every run passed 148/148, 2960 of 2960 in total, taking 39 to 61 s each. The counts are in cold-runs.txt. The 300 ms deadline test is unchanged.

Run 1 started with a queue-commit.sh that differed from the candidate in one comment line, the step-1 comment, which I reworded during run 1. Runs 2 to 20 used the candidate bytes.