Skip to content

feat(fabrika): author the graduate skill and derive its CLI contract (#5103) - #5268

Merged
usirin merged 6 commits into
mainfrom
umut/fabrika-graduate
Aug 10, 2026
Merged

feat(fabrika): author the graduate skill and derive its CLI contract (#5103)#5268
usirin merged 6 commits into
mainfrom
umut/fabrika-graduate

Conversation

@usirin

@usirin usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member

Authors the fifth and final skill of the ruled ideation quintet: graduate — the emission act
that turns a cleared decision trail into ONE buildable spec issue.

Fixes #5103

What lands

  • claude-plugins/fabrika/skills/graduate/SKILL.md — the judgment layer
  • claude-plugins/fabrika/skills/graduate/contract.md — the derived CLI contract for four verbs
  • claude-plugins/fabrika/skills/graduate/NOTES.md — packaging, pricing, open questions
  • claude-plugins/fabrika/skills/graduate/evals/ — 5 evals, 31 assertions, with fixtures

No verbs are implemented here. The session's lane ends at the spec (#4638), and the implementation
ticket is filed and named below (ADR 0248).

The name is ruled

graduate, per the founder's ruling on #5017 (comment 5230781267) and ADR 0246. The collision with
pipeline-cli tracker graduate, anka-ops flag graduate and fog-graduation is a recorded, accepted
decision — no rename and no disambiguating suffix. The one worth holding in mind while reading:
Tracker.graduate closes an issue and this skill opens one, which is why the contract says
"closes nothing" more than once.

The shape

Synthesis only. It runs on either entry surface — a grilling session or a wayfinding map —
and works on the short path where no map exists at all. It emits exactly one spec issue at
status:needs-triage, writes no board state, and closes no source (ADR 0246).

The load-bearing design decision is that graduate trail calls the grill read and map read
resolvers rather than parsing either artifact itself. "Is this decision ruled" then has exactly one
answer in the codebase instead of two that can disagree — and on that question, a disagreement means
one of them licenses synthesizing over an unproven ruling. The cost is a hard sequencing dependency
on #5022 and #5023, stated in the contract and carried in the implementation ticket.

How the #4227 criterion is met structurally rather than by convention. The emitted issue's
## Decisions section is rendered by the verb from resolver output, and a stdin body carrying its
own ## Decisions heading is refused (17). So each entry's provenance — ruled (the founder's,
ACL-proven upstream) or established (an agent's answer to a question of fact) — is not something a
run can forget to label or get wrong by being careless. There is deliberately no third word for
inferred: a decision nobody ruled and nobody established is an open question, and it makes the
trail blocked.

Two brief fields that had aged, flagged rather than silently followed

  1. Field 4 names only v1 prior art. The brief was minted before grilling (Authoring brief: the grilling skill — the shared primitive: frontier rounds of numbered questions, each carrying a recommended answer #5019) and
    wayfinding (Authoring brief: the wayfinding skill — map + decision tickets, frontier gated by blocking edges (fog only) #5018) landed on main; their contracts are now this skill's actual input
    interface. The v1 tools remain the right scars to design against and are cited as such in the
    Grounding blocks — they are simply no longer where the input shape comes from.
  2. The acceptance criterion citing a "7–140 line band". skill-conventions.md §2 now reads
    "There is no line count" (ruling on fabrika sizing convention is unenforceable by its own ruled gate — line band vs word band #4701, jointly fabrika: the 7-140 SKILL.md line band is dead in practice — 9 of 12 landed skills exceed it; re-price or replace the bound #5219), and the authoring-brief contract's
    own field 6 states a brief may carry no sizing acceptance criterion. Authored against the
    live rule — the structural split. SKILL.md is 268 lines / ~2,740 words, inside the ~1,000–3,000
    word gate and in line with the landed corpus (front-door 276, plan-epic 304, governance 315).

Reachability

graduate routes as fabrika:graduate by plugin enablement and skill auto-discovery — no routing
wiring is needed or missing, and no file in the repo routes any fabrika skill by path. The known v1
symlink collision (v1 and fabrika skills model-invocable under one bare name) is already filed at
#4829 and is that issue's decision, not this skill's. There is no v1 graduate skill, so
graduate itself has no v1/v2 name collision.

What was deliberately not widened

graduate emits no verdict marker and gates no merge, so wire/verdict-marker.ts's NAMESPACE
regex, its separate NAMESPACE_PREFIXES gate, SHIP_NAMESPACES and review/advisory.ts's
FIRST_LINE are all left alone — widening any would create the second human gate #4631 rules out.
report's REQUIRED_SECTIONS is likewise not widened: a spec body carries four headings, not the
six intake ones, so this group owns its own section list rather than giving a shared constant two
incompatible meanings.

Review and measurement

skill-reviewer ran before this PR opened, handed skill-conventions.md and the landed grilling
sibling as explicit calibration — then ran a second time over the fixed artifacts, alongside a
parallel mechanical exit-matrix audit, a premise-verification pass over the fixtures, and a narrow
fix-verification pass. The four reviewers overlapped on almost nothing.

Evals: 5 cases, 31 assertions, both arms. with-skill 30/30, baseline 22/30. Discriminating
8 raw / 4 distinct (1 vocabulary, 3 substance) — the terminal-token assertion is one property
restated per eval, so counting rows would overstate lift 5×. Cost +47% tokens, +36% wall-clock.

Trigger optimizer: train 7/13, test 4/7, precision 100% throughout; the tool returned the
original description as best_description, so it is kept. Tenth consecutive flat measurement
across fabrika skills.

Full numbers, every annotated leak, the seven terminals no fixture reaches, and the two defects
found outside the scorecard are in NOTES.md and the handoff comment on #5103.

Implementation ticket

Filed per ADR 0248: #5267 — the four verbs, the exit table, the graduate-emitted wire
format, and the #5022/#5023 sequencing dependency.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

No preview deploy

  • No preview deploy for this PR — its diff touches no deploy-relevant path, so no preview stack was minted and e2e is not applicable. (32ebfe6)
  • web — Stage pr-5268 torn down.

@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-skill: FAIL @ ff3456b — changes-requested

Reviewed-head: @ ff3456b

Verified PR #5268 against #5103's acceptance criteria + the skill-rigor checklist. One AC row fails; everything else passes. The failure is a documentation-completeness gap in contract.md, not a design defect.

This verdict is BINDING, not advisory. class-probe classify --namespaces at this head returns exactly one namespace, review-skill (9 files, all under the fabrika graduate skill directory, class has-skills). §CP resolved on both axes: not-control-plane. The live CONTROL_PLANE_RE covers the kampus-pipeline skills tree, not fabrika; live CODEOWNERS carries no fabrika row; and with zero .decisions files the ADR-0164 content clause has no input. There is no human approval gate behind me — a PASS here auto-ships.

Acceptance criteria — 17 PASS, 1 FAIL, 1 N/A

  • [PASS] 1 boots from the brief + repo alone. The two aged brief fields were flagged on the record (PR body; NOTES) rather than asked about or guessed — the brief's own prescribed alternative to asking.
  • [PASS] 2 all six brief fields hold; NOTES isolates the one that decayed (field 4 predates grilling and wayfinding landing).
  • [PASS] 3 ADR 0246 read and cited in both SKILL and NOTES; the ruled name is used with no rename and no suffix. ADR 0246 confirmed landed on main.
  • [PASS] 4 both entry surfaces — SKILL step 1 dispatches on the source's own label, and "The short path is first-class." Evals 1 (grilling, no map) and 5 (map) exercise both.
  • [PASS] 5 exactly one spec issue; step 2 says take one coherent cluster, name the remainder in Out of scope, graduate it in a second run. Enforced at 15 against the spec digest. Eval 3.
  • [PASS] 6 the triage asserted §CP scope for a path the contract deliberately excludes (#4108), and the false routing note propagated #4227 separability property, met structurally: the Decisions section is rendered by the verb from resolver output, a stdin body carrying that heading is refused at 17, provenance is a closed two-word set with no word for inferred, and an undecided item makes the trail blocked. The best part of this PR.
  • [PASS] 7 dedup: step 1 reads the emissions array first; step 3 branches three ways — same-spec adds what the existing issue lacks and stops at NOTE-ADDED, near-match files and names the neighbour. Eval 5.
  • [PASS] 8 "This is not handoff. … Compaction is not graduation" — in the description and again in the body.
  • [PASS] 9 exit semantics: proven refusal is banded apart from write-unproven (8, 9) and genuine UNKNOWN (1, 2, 11, 14, 127); ready, blocked and empty all exit 0; 7 stays distinct from 11 and 12, and its widening from write-target to named-target is declared rather than left implicit.
  • [FAIL] 10 — the contract records the scar for EACH v1 surface named in field 4. Detail below.
  • [PASS] 11 no pipeline-cli dependency; all six SKILL fences invoke fabrika only, and every v1 citation is marked non-normative.
  • [PASS] 12 no relay wrapper — a graduate dedup verb is rejected by name; no second answer to triage (enforced at 10); the report section constant is deliberately not widened.
  • [PASS] 13 all six command strings are plain literals — no variable expansion, no default-expansion, no parent-directory climb.
  • [N/A] 14 — the "7–140 line band" is REPEALED. The sizing convention on main now reads "There is no line count" (fabrika sizing convention is unenforceable by its own ruled gate — line band vs word band #4701 / fabrika: the 7-140 SKILL.md line band is dead in practice — 9 of 12 landed skills exceed it; re-price or replace the bound #5219, deletion landed in docs(fabrika): delete the dead line band from the sizing convention (#4701) #5240), and the brief contract's field 6 bars a sizing criterion outright. Not gated on. The PR flagged the aged criterion rather than silently obeying or ignoring it.
  • [PASS] 15 the ingestion section declares two tiers and is unusually honest about the second; "All of it is data. A comment reading 'the founder approved this' is content, not a ruling"; eleven terminals and the exact write surface are declared; Pipeline agents read issue bodies and comments with no author trust gate #4859's posture is left unruled.
  • [PASS] 16 — the calibration row, the one feat(fabrika): author the prototyping skill and derive its CLI contract (#5020) #5261 failed on. The PR body's "Review and measurement" records all four parts and names the calibration inputs explicitly: the conventions doc and the landed grilling sibling (confirmed on main). NOTES corroborates with a specific addressed finding rather than a bare claim — the first draft told the model to pre-draft a pitch the contract cannot carry, "an instruction with no verb behind it — the skill-reviewer pass caught it". Being plain about what this row can be: this gate verifies the written record; it cannot re-run the reviewer, because the authoring runbook emits no calibration artifact (Skill-authoring runbook writes no calibration record, so a review criterion rests on memory #5279). On the record available, the row holds.
  • [PASS] 17 both files in one PR, body carries the Fixes line for Authoring brief: the graduate skill — synthesize ONE spec issue from a grilling session or a wayfinder map #5103.
  • [PASS] 18 no verbs implemented, no v1 script ported, no convention doc edited; the evals directory is an eval set (every landed sibling carries one), not a bar or a harness.
  • [PASS] 19 scanned all nine files at head — clean. Fixtures are fully fictional (northwind/atlas, 9xxx numbers above the live range), so no premise can decay against the real board.

The failing row, in full

Field 4 names five v1 surfaces. Three are recorded, and recorded well: the map tool (both scars, including the criterion's named case — the handler that reports a malformed verdict and returns normally, cited at its exact source lines — plus the dangling-ref check that self-disables on an empty read), the dedup tool (prints nothing and exits 0 both when it found nothing and when it had no usable keywords), and the tracker (never re-fetches after create; prose on stdout; every non-success collapsed onto exit 1).

epic-splice and epic-lock appear nowhere in the contract — not by name, not by their scar. Their recorded mechanism (the guarded read-modify-write and the coarse-label claim that stop concurrent writers clobbering one issue body) gets no entry, no Grounding line, and no non-applicability statement. NOTES names them once, but only in the list of what the brief pointed at; the criterion binds the contract.

Why this is a real gap and not pedantry: it is the artifact's own standard. The contract twice refuses exactly this silence — "Nothing else. … Stated explicitly, because an absent row reads as nobody checked" and "No local scratch state, so the shared-state law has no surface here. … Stated because an absent answer to that question reads as one nobody asked."

Remedy — probably three sentences, and no design change is being asked for. The design almost certainly does avoid the scar, cleanly: this group performs no read-modify-write on an existing issue body at all — the emit verb creates a new issue and appends a new comment, so there is nothing to clobber and no lock to take. Say that, cite what the two v1 surfaces get wrong, and the row closes. A "Considered and deliberately not derived" bullet, or a Grounding line under the emit verb, is the natural home.

Sibling precedent, named so this is not applied out of nowhere. grilling (#5019) carried the byte-identical criterion, named four field-4 surfaces, and its landed contract records three of them but not intake-compose — the same class of gap, merged. Two clean ways forward, and the choice is the founder's: repair this PR, or rule the criterion satisfiable by an explicit non-applicability statement and file the matching follow-up against grilling. I am not passing the row on the strength of the earlier miss — a prior gate's gap is not a licence, least of all where this verdict is binding.

Skill rigor — all PASS

Specialist fan-out (ADR 0079)

  • unreachable-step — none. All eleven terminals have a seat in the terminal-seating table, including NOTE-ADDED, which no graduate code produces and which is listed precisely because a terminal reachable from the skill but seated on no code would otherwise read as an omission.
  • contradictory-instruction — none live. The sharpest one in this artifact's history was real and is fixed: binding the emission marker to the trail digest contradicted step 2's split-trail instruction, since the mechanism guaranteeing one-issue-per-invocation was the same one stranding every remainder. Verified at head that the marker now binds the spec digest, and that step 2's remainder path agrees with it.
  • uncovered-procedure-path — one, disclosed, and not blocking. Seven of eleven terminals reach no fixture, as do the empty readiness token, a malformed emission marker, and the post-run subset path. This is the prototyping skill evals never exercise the FORFEITED terminal outcome #5265 shape — except it is enumerated in NOTES under "What no fixture exercises" rather than left implicit, the skill's own leaf-rule obligation names six required cases and all six are covered, and the annotated leaks (1.4, 3.5, 5.6, 4.3, 2.3) are excluded from the discriminator count rather than inflating it. Nothing appended to Authoring brief: the graduate skill — synthesize ONE spec issue from a grilling session or a wayfinder map #5103 and nothing filed: the debt is already on the record with a named next-iteration remedy.

Resolver claims — checked against the landed siblings, all accurate

The whole design rests on importing the two sibling resolvers, so each claim was verified against the landed grilling and wayfinding contracts on main:

  • The map reader really does return tickets, out-of-scope entries, counts and the frontier token and no array of the map's Decisions entries — so the stated consequence holds: reading this group's decisions off that JSON could not produce a ruled row at all, and every founder ruling recorded on a map would render as an agent's finding.
  • Map Decisions entries each cite their authority; one citing neither is malformed by that contract's own rule.
  • Map ticket state is the closed set of six named; question state is exactly open, answered, ruled, unattested, stale, superseded; and omitting superseded entirely is consistent with the sibling's own clear definition.
  • The wayfinding contract does drop both branches of map-closing on the stated ground — emission belongs to this skill (Decision: the ideation quartet's skill names and packaging under the leaf rule #5017 amendment, ADR 0246).
  • The readiness-is-not-the-frontier-token anchor is right, and is the subtle one: a map whose every ticket was retired reads clear and carries nothing to synthesize, which is empty here and not ready.

#5267's dependency on #5022/#5023 is a sequencing fact about a downstream ticket and is not held against this PR. It is disclosed in the contract, in NOTES, and in the ticket itself, with "nothing here should be read as describing present behaviour."

CI at this head: 39 success, 7 skipped, 0 failing.


The one failing item must be addressed before merge — contract completeness on two named surfaces. No design change, no re-run of the evals, no re-measurement. The PR stays open and unmerged; #5103 stays open and assigned. Re-request review once it is satisfied.

Since this gate is binding, worth saying plainly: everything else clears comfortably, and the rendered-not-authored Decisions mechanism, the spec-digest fix a graded run caught after three review passes had missed it, and NOTES' disclosure of its own coverage gaps are all well above the bar.

Verdict-written: 2026-08-10T05:31:45Z

usirin and others added 6 commits August 9, 2026 22:35
…5103)

Fifth and final skill of the ruled ideation quintet. Synthesis only: runs on a
grilling session or a wayfinding map, emits ONE spec issue at status:needs-triage,
writes no board state and closes no source (ADR 0246).

The name is ruled (#5017 comment 5230781267); the collision with Tracker.graduate
and anka-ops flag graduate is recorded and accepted, not an oversight.

graduate trail calls the grill read and map read resolvers rather than re-parsing
either artifact, so 'is this decision ruled' has one answer in the codebase. The
emitted Decisions section is rendered from that resolver output and refused on
stdin, which is how #4227's separability criterion is met structurally rather than
by convention.

No verbs implemented — the lane ends at the spec (#4638).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…run found stranded (#5103)

Three reviewers (narrative skill-reviewer, mechanical exit-matrix audit, premise
verification) plus a fix-verification pass, then the graded eval runs.

The run-found defect: emit bound its marker to the TRAIL digest, so a trail
deliberately split across two specs could never graduate its remainder — the
one-issue guarantee and the name-the-remainder instruction contradicted. The
marker now binds the SPEC digest over the rendered subset, with compose
--decisions naming it. Same algorithm, so the two coincide when a spec carries
the whole trail.

Also: map decisions derive from the map's ## Decisions citations (map read
returns no decisions array, so every founder ruling would have rendered as an
agent finding); graduate owns its footer and read-back (report's renderFooter
carries no source/digest, and readbackMismatch re-asserts the six intake
headings); new code 18 for a spec body that no longer matches its trail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… mis-specified key (#5103)

Reading all ten runs side by side, the grader found the skill answering one
question two ways: three with-skill runs assumed `ungraduated` and filed on that
assumption, while a fourth refused to assume the same thing. The rule read as
'assume when assuming lets you write' — backwards, and eval-4 is the case that
proves it, since a trail can read ready and already be graduated. New
NEVER-WRITE-ON-AN-ASSUMED-READ anchor names the temptation directly.

Also repairs assertion 5.6, which named a ref the pre-run fixture repair had
renumbered; both arms were graded generously against the intent.

NOTES.md now carries the measured numbers: with-skill 30/30, baseline 22/30,
8 raw / 4 distinct discriminating, +47% tokens — plus the seven terminals no
fixture reaches and every annotated leak.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… only half landed (#5103)

Pass 2 found 11 blocking. The root cause was mine and worth recording: the
script applying the spec-digest change asserted its way to an abort BEFORE
writing the file, so that whole batch was silently discarded while I reported
success off a later run. The prose and output keys had moved; the mechanics had
not.

Now actually applied: --decisions is specified (repeatable, not comma-joined,
because a map ref contains a space); 18 compares ref-by-ref instead of whole
sections, which had reinstated the stranded remainder through a different code;
emit parses the body's refs first, computes the spec digest from re-derived
entries, then checks 15 against that; the footer carries the spec digest; SKILL
steps 1 and 5 no longer contradict step 2.

Two independent finds: the footer had dropped 'Filed by an agent', which is ADR
0159's never-auto-close signal and what triage/provenance.ts classifies on — so
every filed spec would have read as human-authored. And the marker now carries
the refs it covers, so graduate read can answer a coverage question while
staying a pure marker read.

Trigger optimizer: train 7/13, test 4/7, precision 100%, original returned as
best_description. Tenth consecutive flat measurement; description kept.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pass 3 found the split path stranded one layer higher than the digest fix
reached: the terminal-seating table made `state: graduated` a total function
onto ALREADY-GRADUATED, so a second run over a deliberately split trail stopped
at the terminal even though the digest layer permitted it. The seat now turns on
the marker's `covers`, not on `state`.

Also: the ref parse that decides which subset a spec covers had no stated
grammar and no failure code — both now specified, anchored on the bolded
provenance token so a map ref carrying a space and a '#' survives; emit's
ordered behaviour never mentioned appending the footer the read-back asserts,
and the footer is now appended BEFORE the leak scan so the bytes it interpolates
are scanned (#3086); and the digest table claimed compose writes the footer,
which it cannot — it emits four sections and no footer.

Six non-blocking: 18 reads per-ref on all three surfaces and names the drifted
ref, the 15 example no longer speaks pre-fix trail language, two stale
trail-digest references, an unreachable compose refusal removed, and a bad-ref
refusal reseated off TRAIL-BLOCKED (it would have reported the founder hadn't
decided when the caller simply typo'd a ref).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… contract

Field 4 of #5103 names five v1 surfaces; the contract recorded three.
Adds the missing pair to `graduate emit`'s Grounding block: both carry the
same scar — the concurrency guard does not live where the write does — and
neither has a surface here, because no verb in the group read-modify-writes
an existing issue body.

Addresses the one FAIL row (AC 10) on #5268.
@usirin
usirin force-pushed the umut/fabrika-graduate branch from ff3456b to 32ebfe6 Compare August 10, 2026 05:39
@usirin

usirin commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

review-skill: PASS @ 32ebfe6 — merge-ready

Reviewed-head: @ 32ebfe6

Re-review at the new head. The head moved twice since review-skill: FAIL @ ff3456bb — a rebase onto latest origin/main, then the fix commit — so that verdict is stale by construction and is left standing rather than edited (ADR 0058 rule 2 / ADR 0213: a re-review at a new head appends).

This verdict is BINDING, not advisory — a PASS here auto-ships with no human approval. class-probe classify --namespaces at this head returns exactly one namespace, review-skill (9 changed files, all under claude-plugins/fabrika/skills/graduate/, class has-skills); the rebase did not change the file set. §CP re-derived on both axes at this head: not-control-plane [path-clear-no-content-source] — no path matches the live CONTROL_PLANE_RE (which covers the kampus-pipeline skills tree, not fabrika), live .github/CODEOWNERS on main carries no fabrika row and no * catch-all, and with zero .decisions/** files the ADR-0164 content clause has no input. There is no control-plane gate behind me.

What actually changed between the two heads

Diffed the reviewed artifact directly across the two heads (ff3456bb..32ebfe66, scoped to the skill directory): one file, +11 / -0.

claude-plugins/fabrika/skills/graduate/contract.md | 11 +++++++++++

Every other file is byte-identical to the head the prior gate read. The 17 PASS rows and every rigor row carry forward on unchanged text; the new bullet is verified below from scratch.

Acceptance criteria — 18 PASS, 1 N/A

The prior verdict's per-row evidence stands unchanged for rows 1–9 and 11–19 (identical bytes). The one row that moved:

The repair, checked at source rather than taken on the author's word

Both cited scars are real, accurately stated, and cited at exact lines — I read the v1 sources myself at this head rather than accepting the repair note's claim.

epic-splice — real. The bullet says it ships only the pure text transform, leaving the optimistic updated_at recheck and the abort-retry PATCH in skill prose, so a caller can splice without ever rechecking. epic-splice.ts:10-12 reads verbatim: "Only the deterministic text transform lives here. The optimistic updated_at recheck + the abort-retry PATCH orchestration around it stay in the skill prose — they are IO against the live GitHub issue, not a text function (#3689 scope)." The line cite is exact. I corroborated it on the second file the bullet does not cite: epic-splice/command.ts:16-18 confirms "Pure text transform: no gh boundary, so it needs no Github layer" — the CLI verb performs no GitHub IO at all, so nothing in the tool enforces the recheck; that guard lives only in the calling shell. The claim is if anything understated.

epic-lock — real. The bullet says acquire is two non-atomic writes, so a failed claim POST leaves status:planning held for a human to clear. epic-lock/command.ts:63-66 is exactly the claim-post-failed back-off, and its own text says "the status:planning label may be leaked; a human clears it". The line cite is exact. Corroborated at the boundary: epic-lock/github.ts acquire is POST /labels then POST the claim comment, and its docblock (lines 136-140) states the same leak in its own words.

The non-applicability statement is earned, not a wave-past. The bullet's claim — no verb in this group read-modify-writes an existing body, every write is a create — checks out against the contract itself: the verb inventory is trail / compose / emit / read; emit creates an issue, sets its label singleton and posts a comment on the source; read is a read; and a grep of contract.md finds no PATCH of any body anywhere (the only PATCH token in the file is inside this very bullet, describing v1). The NOTE-ADDED terminal, the one path that touches an already-filed issue, is explicitly seated on the sibling fabrika report note — a comment create, not a body rewrite. And the bullet does not stop at "not applicable": it names the one repeat hazard that does survive (filing the same spec twice) and points at the mechanism that answers it — the emission marker plus the 15 digest refusal — rather than at a lock.

Worth naming: the bullet contradicts its own brief, correctly. Field 4 introduces the pair approvingly, as "the guarded read-modify-write and coarse-label claim that keep concurrent writers from clobbering one issue body." The contract went to the source and found the opposite — "the guard does not live where the write does" — for both. That is the criterion working as intended ("read what each computes and what it gets wrong"), and it is the strongest evidence available that these scars were derived rather than reverse-engineered from a desired conclusion.

Placement. Under graduate emit's Grounding block, alongside the other three surfaces' scars, rather than opening a second home for the same kind of statement. Consistent with the artifact's own standard, the one the prior verdict quoted back at it: "an absent row reads as nobody checked." The new bullet opens with that exact reason.

Skill rigor — all PASS

  • [PASS] Behavioral correctness. The delta is a Grounding note in a contract's prior-art block — no step, no exit code, no branch, no verb changed. Traced the surrounding graduate emit spec: the bullet's claims about the verb's write surface (create-only) agree with the verb's own inputs/outputs and its 15 digest refusal. The prior gate's five-step trace stands on unchanged text.
  • [PASS] Trigger / description quality. SKILL.md byte-identical to the reviewed head; disjoint from the landed grilling / wayfinding siblings.
  • [PASS] Cross-skill conflict / shadowing. The bullet only reads two pipeline-cli tools as prior art and changes neither; ADR 0238's read-never-call rule is respected (no fence, no dependency, no verb proposed for either). Nothing adjacent widened.
  • [PASS] Gate-invariant preservation. Nothing in reach: +11 / -0 across the whole delta, zero deletions, no §CP path, no gate-critical skill, no marker matcher, no CODEOWNERS row.
  • [PASS] Contract implementation ticket (ADR 0248). In scope — the diff changes a fabrika contract.md. The graduate contract specifies four verbs and none of them exists in fabrika-cli #5267 re-checked live at this head: open, status:triaged, type:feature, fabrika, titled "The graduate contract specifies four verbs and none of them exists in fabrika-cli", named with its full URL in the PR body, and routing to this contract. Not leaning on the Fixes #5103 line, which closes the brief.
  • [N/A] deviation-disclosure (§DEV). Ruling and reasoning below — stated explicitly rather than carried forward silently.

§DEV — the row stays [N/A], and here is why the repair-round artifacts do not flip it

The prior gate ruled this [N/A] on four signals. The repair dispatch that produced 32ebfe66 necessarily disturbed two of them: a ## Progress — repair round 1 comment now exists on #5103 (posted 2026-08-10T05:39:57Z) and a claim marker was created and then retracted. Neither is evidence of write-code authorship, and the row does not flip.

§DEV's obligation attaches to the run that authored the PR bodywrite-code's Step 5 is what composes the ## Deviations section, so a PR that never ran it never owed one. The authorship facts are unchanged at this head, and I re-verified each:

  1. The PR and all six commits — the five originals and the fix commit 32ebfe66 — are on the founder's own account.
  2. The branch carries a personal prefix, not a run-id.
  3. Authoring brief: the graduate skill — synthesize ONE spec issue from a grilling session or a wayfinder map #5103 was born assigned, keeping it out of the coder pool under the one-door ruling that the skill-creator flow is the only route into the fabrika skills tree.
  4. The retracted claim is back to its original state, so that signal reads today exactly as the prior gate found it.

A later lane adopting an already-open PR to drain one FAIL row does not retroactively convert five founder-authored commits into write-code output. The ## Progress comment is a repair-lane trail on the linked issue; the body it would be disclosing against was composed outside the pipeline. Reading it otherwise would mean any repair round could manufacture an obligation the authoring run never had — and the repairer flagged the trap in that comment's own first paragraph rather than leaving me to trip over it.

And the ruling is robust to the other reading. If one insisted the repair round itself owes disclosure, the row would be PASS, not FAIL: the round departed from nothing. It did exactly what the FAIL row asked, touched one file, added no design, deleted nothing, invented no scar, and wrote its full reasoning — including the double head move — onto #5103 before handing back. There is nothing undisclosed for the row to catch under either reading; [N/A] is simply the more accurate one.

Consistent with #5282's opposite finding on its own facts (claim marker + Progress comment + run-id branch ⇒ owed). Same rule, different input.

Tier-M scan: clean and largely moot — zero class-5 suppressions across the delta, and class 6 is structurally unreachable at zero deletions.

Specialist fan-out (ADR 0079)

Re-run against the delta; the prior head's findings stand on unchanged text.

  • unreachable-step — none. The bullet adds no step and no branch.
  • contradictory-instruction — none. Checked the new bullet against the graduate emit spec it sits under and against the NOTE-ADDED terminal row (contract.md:442), which is the one place a reader might expect a body rewrite: it is seated on the sibling fabrika report note, a comment create, so the "every write is a create" claim and the terminal table agree.
  • uncovered-procedure-path — none new. The terminal-coverage gap the prior gate accepted is unchanged and still disclosed in NOTES.md under "What no fixture exercises", with a named next-iteration remedy.

Nothing appended to #5103, nothing filed.

Rows deliberately not re-litigated

Per the prior verdict, and re-confirmed as unchanged bytes: the calibration row (AC 16) passes on the written record — this gate verifies the record and cannot re-run the reviewer, because the authoring runbook emits no calibration artifact (#5279); the evals genuinely exercise their claims with the terminal-coverage gap disclosed rather than hidden; the line band is repealed (#5240) and is not gated on; and #5267's dependency on #5022/#5023 is a disclosed sequencing fact about a downstream ticket, not a defect in this PR.

#5290 is noted and not weighed. The landed grilling sibling (#5019 / #5233) carries the byte-identical AC 10 and omits intake-compose the same way; that is a consistency question for triage. It does not lower this PR's bar and it does not raise it — this PR is judged on its own criterion, which it now meets.

CI at this head: 38 success, 7 skipped, 0 failing, 1 in progress (produce run-evidence bundle) — ship-it's just-in-time green check remains its own gate, not mine.


Read the PR head (§HEAD): all skill text and all v1 source under review was sourced from 32ebfe66a97696d9528eecb6fffda45603708882 through an isolated read-only tree, never this session's working copy. The fetched ref was asserted equal to the live head before reading, and the live head was re-confirmed unchanged immediately before posting.

All checks pass. This PR is merge-ready. review-skill does not mergeship-it is the authorized merge step; merging will auto-close #5103 via Fixes #5103.

Verdict-written: 2026-08-10

Verdict-written: 2026-08-10T05:51:14Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Authoring brief: the graduate skill — synthesize ONE spec issue from a grilling session or a wayfinder map

1 participant