Skip to content

fix(fabrika): make the governance floor bind in CI (#5408) - #5494

Merged
notusirin merged 2 commits into
mainfrom
usirin/governance-floor-binds-5408-4043C6C9
Aug 13, 2026
Merged

fix(fabrika): make the governance floor bind in CI (#5408)#5494
notusirin merged 2 commits into
mainfrom
usirin/governance-floor-binds-5408-4043C6C9

Conversation

@usirin

@usirin usirin commented Aug 13, 2026

Copy link
Copy Markdown
Member

The founder vetoed making fabrika's tree control-plane and substituted a control: a required governance verdict on any diff touching .decisions/, .claude/, .github/ or claude-plugins/. That control did not bind. ship gate derived the requirement correctly, but it reports blocked at exit 0, no workflow ran it at all, and the only thing turning blocked into a stop was a paragraph in the ship skill — so #5293 and #5333 both merged with no governance verdict after the floor shipped.

This adds the missing caller. fabrika ship floor asks ship gate for the one governance namespace and refuses when the verdict is not a head-bound PASS; a new workflow runs it on every PR and relays its exit code. ship gate itself is untouched.

Fixes #5408

What binds now, and on which condition

Half Condition it fires on
ship floor (the decision) the PR's changed files touch a governance root and the governance namespace at the given head resolves to absent, stale or fail → exit 18. A verdict from an author without write+ resolves absent through the ADR 0055 ACL gate, so it reds too. Only a head-bound PASS from an authorized author is satisfied.
.github/workflows/governance-floor.yml (the enforcement) any pull_request, no paths: filter. Every non-zero exit reds the job: 18 means a human owes this PR a verdict; 7 / 11 / 13 mean the floor could not be resolved at all, which is UNKNOWN and never a pass (ADR 0092).

Proving the unbound path is closed, not just that the happy path works

A guard that fires on ABSENT but stays quiet on WRONG is the defect class this repo keeps re-landing (#5416, #4887), so the mutation battery is the point of src/ship/floor-verb.unit.test.ts. Each row is a PR that looks like it carries a governance verdict, and each one still reds on 18:

  • governance: FAIL @ <head> — present, head-bound, and negative.
  • governance: PASS @ <other-head> — present and positive, bound to a tree nobody judged (ADR 0058).
  • governance: PASS @ <head> from an author without write+ — present, positive, head-bound, unauthorized.
  • review-skill: PASS @ <head> and nothing else — a PASS in the neighbouring namespace.

Plus the fail-closed rows: an unreadable ACL, an unreadable file list, a short file list, and a zero-file diff all refuse rather than answering n/a.

Run against live PRs, not asserted. On PR #5481 (a real open claude-plugins/** diff with no governance marker) the verb exits 18 and names the state; on PR #5237 (an apps/web diff) it answers n/a at exit 0. Both transcripts are in the contract's Examples block, copied from the runs. This PR's own governance-floor check is the third data point: this diff touches .github/ and claude-plugins/, so the job is expected to be red here until a governance verdict is posted at head. That red is the acceptance criterion being met, not a build failure.

The mechanism choice, and why the two alternatives lost

Recorded in full at claude-plugins/fabrika/skills/ship/contract.md, section Why a caller verb, and not a new exit code on ship gate. In short:

  • Reseating blocked on the 3+ band inside ship gate breaks the interface convention's answer/refusal split for every existing caller at once, and a refusal cannot report which namespace blocked, because refuse() hardcodes empty stdout.
  • Parsing gate\tblocked in the workflow's run: step puts the decision in bash, which ADR 0228 forbids, and needs a second step to decide whether the floor applies — a second copy of GOVERNANCE_ROOTS that nothing reds when it drifts (packages path filter excludes claude-plugins/skills, so its own corpus pins first run in the merge queue #4604).
  • A caller verb leaves ship gate answering as before, keeps one derivation of the verdict, and lets the workflow relay an exit code and derive nothing.

ship floor is not a second conjunction and decides nothing about enqueue — it reads one namespace. ship gate stays the single merge authority.

Deviations

1. Scope — the title's "both halves" is not what this PR delivers, by the issue's own instruction. Said: the title reads Control substituted for fabrika §CP does not bind (both halves). Did: delivered the enforcement half only. Why: the issue body's Scope after split section states this issue is now the enforcement half only and that the visibility half is #5409. Disposition: no action needed — #5409 owns the digest half.

2. The check is red-but-not-required, and the last step is an operator's. Said: AC 2 asks for a red required check. Did: shipped the job; it is not in the main protection ruleset's required_status_checks list, which today holds exactly ci-required, scan changed files for leaks and validate skill frontmatter (read live). Why: adding a context to a repository ruleset is a server-side control-plane change, not a diff, and not mine to make. What binds today: the red check makes ship checks roll up red at the head, so the shipper refuses to enqueue — the autonomous merge path is closed. A human with admin can still override, which the ruleset entry is what would prevent. Disposition: for the founder, at bank time — add the context a governance-root diff carries a governance verdict at its head to ruleset 17377992. Flagged rather than done.

3. One platform claim is not yet verified at the surface that matters. Said: the job runs with permissions: contents: read, pull-requests: read. Did: verified the whole verb against live PRs with a local gh token, not with a workflow GITHUB_TOKEN. Why: the ACL read (GET /repos/{o}/{r}/collaborators/{u}/permission) is the one call whose availability to GITHUB_TOKEN at these permissions I could not establish from source. Disposition: for the reviewer to judge, and this PR's own run answers it — if the token cannot read the ACL, the job reds on 11 with cannot read the ACL for <login> rather than passing, which is the fail-closed direction; the fix is then a permissions line, and the check on this PR will say so plainly.

4. Prose in the ship SKILL was changed, not just pointed at. Said: AC 6 asks that step 3 no longer stand alone as the enforcement. Did: added a paragraph to step 3 naming the workflow, and a row to the skill's repo-surface table with degrade as the when-missing outcome. Why: the skill still refuses blocked itself, so a repo without the workflow is not broken — what is lost is enforcement against a run that never happened, and that is what the row says. Disposition: no action needed.

`ship gate` already derives the governance requirement from the diff, but it
seats `blocked` at exit 0 and no workflow invoked it, so the floor bound on
nothing but prose in the ship skill — #5293 and #5333 merged with no governance
verdict after it shipped.

Adds `fabrika ship floor`, a caller verb that asks `ship gate` for the one
`governance` namespace and refuses on 18 when the verdict is absent, stale or
fail, plus `.github/workflows/governance-floor.yml`, which relays that exit code
and derives nothing (ADR 0228). `ship gate` is unchanged and stays the single
merge authority.

The mechanism choice, and why a new exit code on `ship gate` and a bash parse in
the workflow were both rejected, is recorded in the ship contract.
@github-actions

github-actions Bot commented Aug 13, 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. (136dbdd)
  • web — Stage pr-5494 torn down.

@usirin

usirin commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

AC-5 evidence — the check is observed red, not asserted red.

The governance-floor job on this PR's own head, run 31658060554, step Decide whether the governance floor is discharged at this head:

ship floor: scanned 8 changed files; 8 declared.
ship gate: scanned 8 changed files; 8 declared.
ship gate: scanned 1 comment; 1 declared.
ship gate: scanned 0 reviews; pagination exhausted.
ship floor: #5494 touches a governance root and its governance verdict at 6e722884369913c3ff68a62aa7742def2acc1a9b is absent — no authorized governance verdict at this head — run the `governance` skill and emit one with `fabrika governance post` (#5408).
##[error]Process completed with exit code 18.

This is a real PR touching claude-plugins/** (and .github/**) with no governance marker, and the check reds for that reason and names it. It clears when a governance verdict is posted at this head — that is the gate working, not a build failure.

One thing this run does not establish: the head carried a single non-marker comment, so ship gate short-circuited before any ACL lookup. Whether a workflow GITHUB_TOKEN at contents: read + pull-requests: read can read GET /repos/{o}/{r}/collaborators/{u}/permission is therefore still open (Deviation 3). It fails closed either way: an unreadable ACL is exit 11 naming the ACL, never a pass — and posting the governance verdict on this PR is what will answer it, since that read then has to happen.

@usirin

usirin commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

review-code: advisory — blocking-set PR (§CP — approval-gated)

PR #5494 is §CP — it touches .github/** (.github/workflows/governance-floor.yml), and pipeline-cli cp-classify classify against the live CONTROL_PLANE_RE returns control-plane [path-match] … BLOCKING (human merge). Live .github/CODEOWNERS on main carries /.github/ @kamp-us/control-plane. My verdict is advisory only: it does not authorize a merge. Under the §CP hard gate (ADR 0135), a @kamp-us/control-plane member approves this at its current head and ship-it then enqueues it (ADR 0048 single merge authority).

Reviewed-head: @ 6e72288

Scope check first. The issue TITLE says "both halves", but the BODY’s Scope after split section narrows #5408 to the enforcement half only — "This issue is now the enforcement half only: make the required governance verdict bind mechanically on fabrika-tree diffs. The visibility half … is split out as #5409". #5409 is open. The delivered scope matches what the body actually asks for. Not incomplete.

Verified PR #5494 against the acceptance criteria of #5408, one at a time:

  • [PASS] AC1 — a CI job reads the governance verdict and fails on absent/stale/fail. .github/workflows/governance-floor.yml job floor runs fabrika ship floor <pr> --sha <head>; floor-verb.ts asks runGate({require:["governance"]}), reads the one namespaces[].state row and seats every non-pass on GOVERNANCE_FLOOR_UNMET = 18. The guard is written as an allowlist (if (state !== "pass")), not a denylist of known-bad states.
  • [PASS] AC1b — binds on WRONG, not merely ABSENT. Reproduced first-party, not taken on report. I ran the suite at this head in a throwaway worktree: 12 passed (12) for src/ship/floor-verb.unit.test.ts, and 302 files / 4625 tests passed for the whole package. Then I mutation-tested the guard in that disposable tree: replacing if (state !== "pass") with if (state === "absent") reds exactly the two WRONG-not-ABSENT cases — "a FAIL at this very head" and "a PASS bound to another head" (2 failed | 10 passed). Those assertions are load-bearing, not decoration. The write+ ACL case is live too: the same fixture with permission: "read" instead of "write" flips a head-bound governance: PASS from satisfied to exit 18 / is absent (the ADR 0055 drop), and a review-skill: PASS @ head with no governance marker also reds. Source restored; the throwaway worktree ended with a clean porcelain status.
  • [PASS] AC2 (the half a diff can deliver) — the failure is a red check, not a comment. The job posts no check-run, status or comment and fails by exit code alone; permissions: is contents: read + pull-requests: read, so it structurally cannot comment. Outstanding human half, not charged here: making the governance-floor context a required check is a repository-ruleset change no PR diff can perform — the same shape as CODEOWNERS being INERT until a ruleset flip. It is recorded in ship/contract.md: "until it is required the check is red-but-not-blocking, which is a weaker state than the ruling asks for and is recorded here rather than glossed". Disclosed, not glossed. See the bank-time note below.
  • [PASS] AC3 — the mechanism choice is recorded with its reasoning. claude-plugins/fabrika/skills/ship/contract.md §"Why a caller verb, and not a new exit code on ship gate" records the chosen caller-verb mechanism and both rejected alternatives (reseating blocked on the 3+ band; parsing the gate line in bash). governance/contract.md §1c points at that one anchor rather than restating it.
  • [PASS] AC4 — fails closed. Verified each refusal path first-party: unreadable changed-file list → PRECONDITION_UNKNOWN (11) with the never "n/a" wording; a short list (k < changed_files) → INCOMPLETE_SCAN (13); a zero-file diff → ZERO_SCOPE (7) rather than n/a (ADR 0092); an unreadable ACL → 11; a ship gate answer with no resolvable governance row → 11; any non-zero from ship gate is relayed unchanged. Mutation-tested: disabling the zero-file and short-list guards reds those two tests, so both bind. n/a is only ever reached after a count-checked, non-empty file list proves no governance root — never from an unreadable input.
  • [PASS] AC5 — observed red, not asserted red. Run https://github.com/kamp-us/phoenix/actions/runs/31658060554 exists, head_sha = 6e722884369913c3ff68a62aa7742def2acc1a9b (this PR’s own head), event: pull_request, conclusion: failure. Its log reads ship floor: #5494 touches a governance root and its governance verdict at 6e72288… is absent — no authorized governance verdict at this head … then Process completed with exit code 18. Confirmed against the API myself, not relayed.
  • [PASS] ADR 0228 — the workflow relays, it does not derive. The run: step is one command whose only output that matters is its exit status; nothing in YAML decides which paths are governance-bearing, which verdict is in force, or whether an author may author one. The no-paths: reasoning is sound: a YAML paths: list would be a second copy of GOVERNANCE_ROOTS that nothing reds when it drifts, and a path filter matching nothing presents as a pass (packages path filter excludes claude-plugins/skills, so its own corpus pins first run in the merge queue #4604) — the fail-open direction. The verb’s own count-checked read of the changed files is the single path derivation, and a non-governance diff is a proven n/a at exit 0. Confirmed GOVERNANCE_ROOTS is imported from review/classes.ts, not re-typed.
  • [PASS] no second authority — ship floor reads one namespace and decides nothing about enqueue; ship gate is untouched by this diff and stays the single merge authority.
  • [PASS] exit-table hygiene — 18 is a new seat in the one shared ship/codes.ts table with a doc block saying why it is not folded into 16, and the contract’s shared-taxonomy matrix, the per-verb table and the help text all carry it. The 13 row gained floor. The full package suite is green, so the alignment and help-text guards agree.
  • [PASS] convention — the PR-number / head-SHA interpolation into a run: step matches the existing fabrika-eval-gate.yml precedent verbatim; both values are platform-issued integer/hex, not attacker-controllable text, so there is no injection surface. Action pins (actions/checkout@v4.2.2, pnpm/action-setup@v4.1.0, actions/setup-node@v4.4.0) match the house style. The event is pull_request, not pull_request_target — a fork PR gets a read-only token.
  • [PASS] CI at head — 44 checks green. The only two reds are (a) this PR’s own governance-floor job, which is AC5’s evidence and clears when a governance verdict is posted at head, and (b) fabrika grade gate (90% graded; floor + spend report not yet enforced), which the author declared as a separate handoff and is not this diff’s finding.

Run-evidence bundle: PRESENT for head 6e722884 — producer run 31658060486, artifact 9165220185, manifest.commit == head, schemaVersion 1; checks 2/2 pass; tests 2424/2424 passed, 0 failed, 0 skipped.


On the author’s UNVERIFIED platform claim — flagging was right, and the fail-closed claim holds

The author flagged rather than asserted whether a workflow GITHUB_TOKEN at contents: read + pull-requests: read can read GET /repos/{owner}/{repo}/collaborators/{username}/permission. Flagging was the correct call — that is exactly the "ground falsifiable platform/dependency claims in source, not intuition" rule; asserting it from intuition would itself have been the defect.

Two separate findings:

  1. The fail-closed claim HOLDS, and I checked it in code rather than accepting it. An unreadable ACL cannot produce a pass on any path: the errOut("HTTP 403") fixture resolves to PRECONDITION_UNKNOWN (11), that test is present and green, and the only exit-0 answers in runFloor are satisfied (a head-bound PASS from an authorized author) and n/a (a proven non-governance diff after a count-checked read). The worst case is a false red, never a false green.
  2. The question is still open, and it matters at bank time. I confirmed the author’s account that run 31658060554 short-circuited before any ACL call — its log shows scanned 1 comment; 1 declared and resolves straight to absent, with no candidate marker to authorize. So this run does not answer it. If the token genuinely cannot read that endpoint, the check would be permanently red even for a PR carrying a valid governance verdict — an unclearable gate a team learns to ignore. Not a defect in this diff; a precondition for the ruleset step.

Bank-time notes for the approver (not charged against this PR)

  • Ruleset step (the author’s disclosed Deviation 2). Adding governance-floor as a required context is yours. Today the red closes the agent merge path but an admin override can still land the PR.
  • Settle the ACL question before making it required. The first governance-root PR that actually carries a governance: PASS @ head from a write+ author exercises the ACL call for real. Green settles the claim; a red at exit 11 means the workflow needs a token that can read the permission endpoint. Requiring the context before that observation risks a permanently-red gate.
  • Blast radius. GOVERNANCE_ROOTS is .decisions/, .claude/, .github/, claude-plugins/. That is pre-existing (feat(fabrika-cli): make the governance verdict a floor ship gate raises from the diff (#5036) #5231) and single-sourced, and binding it is exactly what the ruling asked for — but from this merge onward every ADR PR and every pipeline/skill PR in this repo reds this check until a governance verdict is posted at its head. Worth knowing before the ruleset flip.

Verdict-written: 2026-08-13T01:55:31Z

@usirin

usirin commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

review-skill: advisory — blocking-set PR (§CP — approval-gated)

PR #5494 is §CP — pipeline-cli cp-classify classify against the live CONTROL_PLANE_RE returns control-plane [path-match] on .github/workflows/governance-floor.yml, and live .github/CODEOWNERS carries /.github/ @kamp-us/control-plane. (The fabrika skills tree itself is deliberately not §CP — the 2026-08-10 veto, and this diff does not widen that boundary.) My verdict is advisory only: it does not authorize a merge. A @kamp-us/control-plane member approves at head, then ship-it enqueues (ADR 0135 / ADR 0048).

Reviewed-head: @ 6e72288

Behavioral surface under review: claude-plugins/fabrika/skills/ship/SKILL.md, claude-plugins/fabrika/skills/ship/contract.md, claude-plugins/fabrika/skills/governance/contract.md. Verified against #5408 (whose BODY narrows it to the enforcement half only, with the visibility half split to the open #5409 — the delivered scope matches):

  • [PASS] AC6 — the SKILL step-3 prose no longer stands alone as the enforcement. The inserted block says plainly that the reading of blocked "is no longer the only thing enforcing the governance floor", names .github/workflows/governance-floor.yml, and states the non-obvious property an agent needs: the step and the job resolve the same verdict through the same ship gate, so they cannot disagree. The remedy it points to (route to the governance skill) is the one action that clears both.
  • [PASS] AC3 — the mechanism choice is recorded under claude-plugins/fabrika/, once. ship/contract.md gets a <a id="the-mechanism-choice"> section with the chosen option and both rejected ones, each with its own reason (the answer/refusal split for blocked; ADR 0228 forbidding a bash-side decision; the packages path filter excludes claude-plugins/skills, so its own corpus pins first run in the merge queue #4604 drift risk of a second GOVERNANCE_ROOTS copy). governance/contract.md §1c links to that anchor rather than restating it — one source, one reader, no second copy to drift.
  • [PASS] contract completeness — ship floor is documented in the shape every other ship verb uses: invocation, input table, output grammar, exit-status table (7/11/13/18), a full stderr message table with each message mapped to its code and kind, a scope statement, worked examples with echo $?, and a grounding list. The shared exit matrix gained the 18 row and the 13 row gained floor, so the taxonomy stays single-sourced.
  • [PASS] the WRONG-not-ABSENT property is documented, not just implemented. ship/contract.md carries an explicit four-row table (no marker / FAIL at head / PASS at another head / PASS from an author without write+) with what each resolves to and why it still reds — and I confirmed first-party that each of those four rows has a live unit test that actually binds. Mutating the guard to fire on absent only reds the FAIL and stale rows (2 failed | 10 passed), so the documented claim is true of the code, not aspirational.
  • [PASS] honesty about the weaker state — the contract says outright that making the context required is a human ruleset change and that "until it is required the check is red-but-not-blocking, which is a weaker state than the ruling asks for and is recorded here rather than glossed". A skill doc that claimed full enforcement here would be the defect; this one does not.
  • [PASS] the failure-mode row added to the SKILL dependency table classifies the new dependency as degrade with the correct reasoning (the skill still refuses a blocked namespace itself, so a missing job costs enforcement-against-an-unrun-session, not correctness), names the file, and points at front-door — matching the shape of every other row in that table.
  • [PASS] doc hygiene — repo-relative markdown links only (../ship/contract.md#the-mechanism-choice resolves to a real <a id>); no wikilinks, no home/absolute/sibling-repo paths, no operator names or PII. The repo dead-internal-link check and the skill-corpus lint are both green at this head.
  • [PASS] no boundary widening — nothing here adds a CODEOWNERS row for claude-plugins/fabrika/** or argues for widening CONTROL_PLANE_RE; governance/contract.md §1c is appended above the existing veto paragraph and leaves it intact. That is what Control substituted for fabrika §CP does not bind (both halves) #5408 explicitly asked for.
  • [PASS] no rival derivation introduced in prose — the docs consistently point at ship gate as the single verdict authority and at review/classes.ts as the single GOVERNANCE_ROOTS source, rather than restating either list in markdown.

Operational note for the approver, not a criterion charge: once this lands, every PR touching .decisions/, .claude/, .github/ or claude-plugins/ — which is most pipeline and ADR work in this repo — reds this check until a governance verdict is posted at its head. That is what the ruling asked for, but it is a real change in day-to-day flow and worth knowing before the ruleset flip makes the context required. Detail and the open ACL question are in the review-code advisory on this PR.

Verdict-written: 2026-08-13T01:55:40Z

@usirin

usirin commented Aug 13, 2026

Copy link
Copy Markdown
Member Author

governance: PASS @ 136dbdd — no contradiction with standing law, no gate invariant weakened; re-judged at this head after the merge of main

Re-judged from scratch at the new head after the merge of main moved it off 6e722884. This is not
a re-bind of the earlier verdict: scope, guards and base were re-run at 136dbdd9, the whole
diff was re-read against merge-base 39851a6e, and the two claims the change rests on were
re-verified independently.

Namespace. governance scope at 136dbdd9: required, 8 of 8 declared files partitioned,
roots .github/ (1) and claude-plugins/ (3), self true. This is the governance-namespace
derivation, not a §CP classification — whether this diff needs a code-owner approval is a separate
question CODEOWNERS answers.

Self fence. self true — the diff edits governance/contract.md. governance base resolved the
merge base to 39851a6e, and the checkout this run judged from is at exactly 39851a6e, so the
rules applied are the base revision's governance/SKILL.md bytes, not the head's. The head's own
edit to governance/contract.md is one additive paragraph (1c, "…and the requirement had to be
readable by something other than an agent. LANDED") recording what shipped; it changes no rule this
judgement was made under.

Corpus half — no contradiction

No decision record is in this diff, so there was no subject to rank and no sweep was run. The
corpus half is a hand read of the questions the change decides:

Question the change answers Standing law read Result
May CI withhold a merge for a missing governance verdict on a harness diff? the founder's veto-and-substitute on #5036 (2026-08-10); ADR 0058 (SHA-bound verdicts); ADR 0055 (write+ authorization) consistent — this is the substituted control being made to bind, not a new bar
May the decision live in the workflow's run: step? ADR 0228 live (amended-in-part by 0229) consistent — the job relays one exit code and derives nothing
Is the combination in ship floor a derive? ADR 0229 accepted — total, mechanical, UNKNOWN-propagating relay: every gate outcome has a defined seat, every unreadable input propagates as a refusal, no threshold or tie-break is introduced
Does it re-derive "is this a governance root" a second time? ADR 0229's same-question test no — ship floor and ship gate both read the single-sourced touchesGovernanceRoot; one derivation, two readers
What does an unreadable input mean? ADR 0092 consistent — 7/11/13 all red, n/a is reserved for a proven non-governance diff, and four unit rows pin it
Does this become a second merge authority? ADR 0053, ADR 0135 no — gate-verb.ts is untouched, runGate is called with cp: false, and the verb decides nothing about enqueue

Cited as live via adr resolve at the bound commit: 0228, 0092, 0058, 0055, 0135, 0053, and 0229
read in full.

One citation nit, not a finding. The ship contract rejects the "parse gate\tblocked in bash"
alternative citing ADR 0228 alone. 0228's status carries amended-in-part by 0229, and 0229 is where
the combination rule now lives. The conclusion survives the amendment — 0229 explicitly bans "a regex
over a verb's stdout that reinterprets it into a different category", which is exactly what turning
an exit-0 answer into a job failure would be — and the second stated reason (a YAML copy of
GOVERNANCE_ROOTS that nothing reds when it drifts, #4604) is independent of either ADR. Worth a
pointer to 0229 on a later pass; it does not change what was decided.

Gate half — no weakening

governance guards at 136dbdd9: no-anchor-change, 8 files scanned, 2 anchored invariants in
reach, both in claude-plugins/fabrika/skills/ship/SKILL.md. The anchored invariants in reach are
DISARM-LIFECYCLE and CAPABILITIES; neither is touched by this diff.
Both hunks in that file are
additive and sit outside the anchored text: a paragraph after step 3's "Absence and staleness are
refusals, never passes", and one row in the required-repo-files table.

Read by hand for the unanchored half, since an unanchored invariant is invisible to the scan:

  • The new step-3 paragraph does not relieve the skill of its own refusal. It states the job and
    the skill resolve the same verdict through the same ship gate and so cannot disagree. The
    standing sentence "Absence and staleness are refusals, never passes (docs(patterns): the Dirent-vs-FileSystem.stat lstat trap in effect-platform-access (#3922) #3944, ADR 0058)" is left
    intact directly above it.
  • The new required-repo-files row disposes a missing governance-floor.yml as degrade, and says
    in terms that the skill still refuses a blocked governance namespace itself. The skill-side guard
    survives the job's absence — the opposite of the "still reads as a guard, no longer guards" shape
    this half exists to catch.
  • ship/codes.ts allocates a new seat, 18, and moves nothing. 12, 13, 16, 17 keep
    their triggers and their verb lists; 13 only gains floor as a producer. No refusal was
    reclassified.
  • ship floor refuses on WRONG, not only on MISSING: absent, stale, fail, and a head-bound
    PASS from an author without write+ all seat 18, each with a unit row. A `review-skill: PASS @ ` in the neighbouring namespace also reds. `governanceState` validates the row rather than casting it, so a payload missing the row is `11`, never a silent pass.

No gate invariant in this diff's reach is removed or softened. The change is a strengthening: a
control that previously bound on nothing but prose now reds a check.

What main brought in, and whether it interacts

The only content in this head that was not in the head judged before is the merge of main, carrying
#5499 (demote the eval gate's graded leg to never-red) and #5500 (declare the kampus marketplace).
Checked deliberately, because #5499 touches an adjacent gate:

The two claims the change rests on, verified here

  • The fail-open was real. feat(fabrika): author the handoff skill and derive its CLI contract (#5021) #5293 is MERGED (2026-08-10), its file list is entirely under
    claude-plugins/fabrika/skills/handoff/ — a governance root — and its comments carry zero
    governance: markers. feat(fabrika): author the write-pattern skill and derive its CLI contract (#4710) #5333 likewise carries zero. Read live, not taken from the PR body.
  • The ACL deviation the PR flagged is now answered by its own run. Deviation 3 asks whether
    GITHUB_TOKEN at contents: read + pull-requests: read can read
    GET /repos/{o}/{r}/collaborators/{u}/permission. This PR's governance-floor job at this head
    exits 18 reporting the namespace as stale — which is only reachable after a marker
    survives the ADR 0055 ACL gate. Had the ACL read failed, the run would have been 11. The
    permission set is sufficient, proven on the real platform rather than asserted.

Routed, not judged here

  • Deviation 2 stands and is correctly flagged, not glossed. The check is not in the main protection ruleset's required contexts, so an admin can still merge past it; the contract records
    that this is "a weaker state than the ruling asks for". Adding a ruleset context is a server-side
    act, not a diff — for the founder at merge time, and outside what this verdict can discharge.
  • Acceptance criteria and code quality are review's; nothing here was folded into this verdict.

Verdict

PASS. No contradiction with standing law, and no gate invariant removed or softened. Everything
this judgement rests on was read: the diff at 136dbdd9, the base revision of this skill's own text,
the six cited records at the bound commit, ADR 0229 in full, both exit tables, and the live evidence
for both of the change's factual claims. Nothing was unreadable, so PASS is reachable.

Stated plainly, since a prior PASS existed at the old head: I did not inherit it. Every check above
was re-run or re-read at this head, and the two places I went looking hardest for a FAIL — the
0228/0229 amendment and the #5499 adjacency — are written up above with what I found rather than
with what I hoped.

@notusirin
notusirin added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 38e89a7 Aug 13, 2026
48 of 49 checks passed
@notusirin
notusirin deleted the usirin/governance-floor-binds-5408-4043C6C9 branch August 13, 2026 03:38
@github-actions github-actions Bot mentioned this pull request Aug 13, 2026
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.

Control substituted for fabrika §CP does not bind (both halves)

2 participants