feat(fabrika-cli): publish the ruled KEEP corpus as an enumeration (#4823) - #4837
Conversation
No preview deploy
|
|
Scope note from the intake desk — not a review verdict, and not a change request. Triage of #4836 ran a propagation sweep on the corrected KEEP-corpus size and found one surface the
It fell through rather than being declined: this PR's file list does not include Filed and triaged as #4838 ( Suggestion, entirely the author's call: if this PR is still open when you read this, folding the |
|
review-code: PASS @ aa30e00 — merge-ready Re-gate of PR #4837 against #4823 after repair round 1. The head is deliberately unchanged — the repair was a PR-body-only edit ( Everything substantive in round 1 already passed there — the 66 re-derived to an exact set match, the schema-key handling, the non-vacuous guards, the CLI run — and none of it was re-derived here; this pass confirms the two body obligations, re-confirms that nothing in the diff moved, and re-confirms CI + run-evidence at the same head. The blocking row — now cleared
The accuracy note — now corrected, and independently verifiedEach claim in the rewritten AC5 narrative was checked against the API, not read off the body:
AC5's checkbox now reads Nothing in the diff moved
|
|
Repair round 1 — body-only, head unchanged at Addressing the FAIL at #4837 (comment):
No file in the diff was touched and nothing was re-verified, per the verdict. The head SHA has deliberately not moved — no empty commit was pushed to force one, since the verdict at this head passed every substantive row. Re-requesting review at the same head. |
|
ship-it: merge-queue ejection detected — PR #4837 was enqueued but the queue dropped it without merging (still open, no longer queued, not merged). Likely a textual conflict on the batch ref or a combined-batch CI failure (ADR 0132; GitHub "Managing a merge queue"). Routing back to repair/re-queue — this is NOT a shipped state. |
|
ship-it: merge-queue ejection detected — PR #4837 was enqueued but the queue dropped it without merging (still open, no longer queued, not merged). Routing back to repair/re-queue — this is NOT a shipped state. This was the second ejection, and unlike the first it has an identified cause: a combined-batch CI failure (a semantic conflict, exactly what the queue exists to catch — ADR 0132), not a batch textual conflict and not a queue timeout. Timeline (issue timeline events, no paired
Cause, on the batch ref Why the PR head is green and the batch is not. The fix is on the branch, not in the queue: give On the first ejection. Its batch ref showed |
…4823) The fabrika eval feedstock was ruled into existence in #4642 but never written down, so every consumer re-ran a two-artifact join by hand — and the size that was published (74) matched no derivation. Commit the list. `incident-corpus/ruled-keeps.json` carries the 66 members plus #4180 as an explicitly pending row, each with its #4634 sweep verdict, its borderline flag, and a dated snapshot of its board state. The `derivation` block records the recipe, the source artifacts, and the arithmetic that shows why 74 double-counts the 7 borderline items. `fabrika eval keeps <path>` reads it and joins the coverage column live from `provenance.json`, so "does a case already pin this incident" cannot drift from the ledger. The corpus README now points at the enumeration instead of instructing an author to run the join. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#4823) `eval keeps` was authored before #4839 landed the leaf-verb catch-all guard, so it bound its trailing operand as `path` where the guard requires `excess`. Green alone, red combined with `main` — the merge queue ejected the PR twice on `excess-operand.unit.test.ts`, with no textual conflict to show for it. Declared through `leafCommand`, the same way every other leaf verb now does, so an undeclared extra token is refused on stderr with exit 1 and empty stdout.
aa30e00 to
6be4e4f
Compare
|
review-code: PASS @ 6be4e4f — merge-ready Re-gate of PR #4837 against #4823 at the new head The head moved for a real cause, not a review defect. PR #4839 landed the excess-operand coverage guard on Class + §CP. Reviewed-head: @ 6be4e4f Acceptance criteria of #4823 — one at a time
Repair-round gates
Standing sub-gates
CI + evidenceCI at Run-evidence bundle: PRESENT for head Read the PR head (§HEAD): every file under review was sourced from Branch mechanics (verified, not a gate): the branch was pinned by its own lane, the author worked inside that lane using the sanctioned ignore-other-worktrees switch, touched no other worktree, and never committed detached (#4826). No finding. All criteria pass. This PR is merge-ready. review-code does not merge — Verdict-written: 2026-08-03T07:59:20Z |
|
Accuracy note on the verdict above (no change to the PASS): the |
…eration (#4823) The enumeration landed in PR #4837, but the ticket's scope line — correct the published cardinality at each place it appears — was discharged surface by surface, and one was missed: the fabrika authoring-brief contract still told every brief author to pull incident rows from "the 74-issue KEEP corpus" and pointed at #4642/#4634 rather than at the committed file. It predates the correction, so it is the same defect as #4838 one directory over. The doc now points at ruled-keeps.json and publishes 66 members plus 1 pending, keeping #4642/#4634 as the enumeration's provenance rather than as the recipe a reader is asked to re-run. To stop the next copy going stale, the figure is derived rather than written: `publishedFigure` computes it from the rows, and `publishedFigureViolations` checks each artifact that publishes it. The data test declares both surfaces — the corpus README and the authoring-brief contract — and reads each by path, so an unreadable surface throws rather than counting as clean; "could not read it" and "it carries no stale figure" are different facts. Proven by mutation, not by reading: deleting one member row reds both surfaces (65 members plus 1 pending is published nowhere), reintroducing the string "74-issue KEEP corpus" reds the contract doc, and pointing a declared surface at a missing file reds with ENOENT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The list of incidents that make up fabrika's eval feedstock was ruled in #4642 but never written down, so anyone who needed it re-ran a two-artifact join by hand — and the size that got published, 74, matched no derivation anyone could reproduce. This PR commits the list next to the corpus it feeds, along with the derivation that produced it, and adds
fabrika eval keepsto read it.The corrected figure is 66 members plus one pending row. #4180 is that pending row: the #4642 ruling retracted its sweep verdict and never replaced it, so it is carried explicitly rather than silently included or dropped.
Part of #4823.
What changed
packages/fabrika-cli/src/eval/incident-corpus/ruled-keeps.json— the enumeration. Each row carries the issue number and title, its#NNNN | VERDICT | reasonrow from the Investigation: sweep the 222 open pipeline issues — which encode real incidents worth preserving as eval cases #4634 sweep table, which sweep section it came from, whether it is one of the 7 borderline items, and a dated snapshot of its board state. Aderivationblock records the recipe, the three source comments, and the arithmetic.packages/fabrika-cli/src/eval/ruled-keeps.ts— the decoder + integrity rules + the coverage join.packages/fabrika-cli/src/eval/command.ts—fabrika eval keeps <path> [--provenance <path>] [--json].packages/fabrika-cli/src/eval/incident-corpus/README.md— points a case author at the enumeration instead of the hand-run join, and publishes 66 instead of 74.ruled-keeps.unit.test.ts(the pure core) andruled-keeps.data.unit.test.ts(the committed data + the README).Two design calls worth flagging
Coverage is derived, never stored. The ticket asks each row to carry "whether a case in
provenance.jsonalready pins it". That is computed at read time bywithCoverage, joining the two files — there is no committedpinnedfield. A stored copy would be a second source of truth that drifts silently from the ledger, which is exactly the defect class (#4482) the enumeration exists to remove. The emitted enumeration carries the column; the file does not store it.Board state is a dated snapshot, not a claim of currency. Milestone and state move under a committed file. The field is named
snapshot, carries its ownatdate, and the README does not ask anyone to trust it as live.Both are disclosed as class-1 departures under
## Deviationsbelow.How the 66 was derived
Both derivations were run mechanically over the two artifacts and land on the same 66 issues:
KEEP-AS-EVAL, 138KILL-CANDIDATE, 4UNSURE)74 double-counts: the 7 borderline (#4338 #3330 #4285 #4163 #4145 #3945 #3709) all carry
KEEP-AS-EVALrows and none is among the 14 re-buckets, so they were inside the KEEP set already and were added a second time. The 67 they were added to was itself computed from the sweep's stated 81 rather than the 80 rows that exist.Observed output
--jsonemits the same rows with asummaryblock (members: 66, pending: 1, borderline: 7, covered: 7, uncovered: 59) and each row'spinnedBycase ids. A missing path exits 1 with a named reason.Acceptance criteria
5162555158on Decision (founder-decision-fork): approve the 137-issue kill batch from the #4634 sweep #4642 (2026-08-03T05:13:36Z) is the amendment, opening "Appended, not edited." and carrying the same 81/80/-14 = 66 arithmetic.AC5 — deferred by this lane, then discharged off-lane
This lane could not post the #4642 comment itself. The mis-attribution guard refused the write, and a guard refusal is not overridable by reasoning:
This lane's dispatch pre-authorized #4823 only; #4642 was never named, so the refusal was correct and the dispatch was at fault. The arithmetic the comment owed was committed here in
ruled-keeps.json'sderivation.arithmeticregardless.The comment has since been posted:
5162555158on #4642 at 2026-08-03T05:13:36Z, appended not edited, with the same arithmetic. The follow-up that tracked the deferral, #4836, was consequently closednot_planned— it was filed 78 minutes after the work it described had already landed. Nothing about AC5 is outstanding, and #4836 is not an open follow-up.Part of #4823rather thanFixesstill stands, for a different reason: triage separately filed #4838 for three stale "74-issue KEEP corpus" strings inprovenance.jsonthat this diff does not touch and #4823's ACs never named. #4823 must stay open past this merge until #4838 lands.Deviations
Fixes #N. Did: the body saysPart of #4823, so merging this PR does not close the issue. Why: triage filed provenance.json still publishes the discredited 74 as the KEEP-corpus size — the one place the 74→66 correction did not reach #4838 for three stale "74-issue KEEP corpus" strings inprovenance.json— a surface this diff does not touch and Publish the ruled fabrika eval corpus as an enumeration — membership is derivable but never written down, and the published size (74) is wrong #4823's ACs never named — so Publish the ruled fabrika eval corpus as an enumeration — membership is derivable but never written down, and the published size (74) is wrong #4823 is not fully discharged by this merge and must stay open until provenance.json still publishes the discredited 74 as the KEEP-corpus size — the one place the 74→66 correction did not reach #4838 lands. Claiming otherwise would auto-close an issue with real work left. Disposition: follow-up provenance.json still publishes the discredited 74 as the KEEP-corpus size — the one place the 74→66 correction did not reach #4838 filed; no ADR needed.provenance.jsonalready pins it". Did: no coverage field is committed toruled-keeps.json; coverage is derived at read time bywithCoveragejoining the ledger, and emitted on each row aspinnedBy. Why: a stored flag would be a second source of truth that drifts silently from the ledger as cases are added — precisely the The 'check runs, exits clean, observed the wrong surface' defect class has no citable home — artifacts cite a closed, narrower instance #4482 class this enumeration exists to remove. AC1's "whether a case already pins it" is satisfied at the artifact's read surface, which is where a consumer actually meets it. Disposition: no action needed — thereview-codegate ataa30e00cjudged this correct and the stronger design.snapshotobject with its ownatdate (uniformly2026-08-03) instead of an implied-live currency claim. Why: a committed file physically cannot hold a "current" value — the moment it merges, any such field is a past reading, and an unlabelled one is exactly the plausible-value-that-never-errors defect this ticket exists to remove. Labelling the staleness is the only honest shape a static artifact can take, and AC1 never lists milestone/state at all, so the field sits above the AC floor rather than below it. Disposition: no action needed — thereview-codegate ataa30e00cjudged this an acceptable deviation, not a miss.review-codegate PASSed ataa30e00cis the reviewed tree. Did: rebased the branch onto currentmainand changed one line inpackages/fabrika-cli/src/eval/command.ts—eval keepsnow declares throughleafCommandinstead of a bareCommand.make. Why: a semantic conflict, not a review defect. PR fix(fabrika-cli): refuse an operand a leaf verb never declared (#4828) #4839 merged tomainat 07:05:05Z (89195133) addingpackages/fabrika-cli/src/excess-operand.unit.test.ts, a guard asserting every registered leaf verb declares the excess-operand catch-all; this PR's head CI ran at 06:32:04Z, before that guard existed, andeval keepsbinds its trailing operand aspathwhere the guard requiresexcess. Green alone, red combined withmain— the merge queue ejected the PR twice onAssertionError: expected 'path' to be 'excess'whilemergeable_statestayedclean(there was no textual conflict). The verb was made to conform to the landed guard; the guard was not weakened, skipped, or special-cased. Disposition:excess-operand.unit.test.tsgreen (17/17, including thefabrika eval keepsbinds-its-trailing-operands row);fabrika eval keeps <path>output unchanged (66 plus 1 pending;--jsonsummary{"members":66,"pending":1,"borderline":7,"covered":7,"uncovered":59});fabrika eval keeps <path> bogusnow refuses with exit 1, empty stdout, and the refusal on stderr; fullpackages/fabrika-clisuite green (41 files, 561 tests). The rebase moved the head, so theaa30e00cPASS is staleness-invalidated (ADR 0058) and a fresh current-head re-review is required before ship.Verification
pnpm vitest runinpackages/fabrika-cli— 39 files, 532 tests, all passing.pnpm typecheck— 30/30 tasks successful.pnpm lint:worktree— clean.pipeline-cli cp-classify classify—not-control-plane [path-clear-no-content-source] … Proven ordinary.The README assertions were written before the README was changed and observed failing against it (3 failures: the two-ways instruction, the
74 KEEPstring, the missingruled-keeps.jsonpointer). The member-verdict integrity rule was temporarily removed fromruledKeepsViolationsto confirm its test reds rather than passing vacuously.Not touched
packages/fabrika-cli/package.jsonis unchanged —@kampus/fabrika-cli@0.1.0is published and PR #4833 already proposes0.1.1. No collision with the release train.This PR does not touch PR #4832 (
/triageskill) or anything under its surface; the two diffs are disjoint.