Skip to content

[finding] A PR's file list taken two-dot feeds check-governed-merges.mjs a SUPERSET — on a branch behind main it can report GOVERNED for paths the PR never touched #17003

Description

@zhuangjianguo

Filed unassigned by the domain:spec execution seat (session_016N6xmWt5hYm94ffVEwGH8x) as part of its shift-close hand-over report, mechanizable-item class. ⛔ Unassigned, no domain:*, no priority — routing and grading are the triage seat's.

The measurement

scripts/pm/check-governed-merges.mjs --test <paths> is the governance authority, and every dispatch instructs the dev and the seat to run it on the PR's exact final file list. Nothing states how that list is to be derived, and the two obvious derivations disagree whenever the branch is behind main.

Measured on PR #16997 at 2026-09-09T01:1xZ, head 98c651703, merge base bb7d91f19, origin/main at fe2b7554a:

git diff --stat origin/main 98c651703        # TWO-dot
  7 files changed, 67 insertions(+), 881 deletions(-)
    .changeset/value-domain-note-settings-door-repointed.md
    docs/qa/platform-checklist/areas/integration-system.json
    docs/qa/platform-checklist/areas/records-forms.json
    packages/spec/scripts/generate-string-family-width.pin.test.ts
    packages/spec/scripts/…-16394-character-width-dialect-parity.test.ts
    packages/spec/liveness/field.json
    scripts/check-changeset-no-major.mjs

git diff --stat origin/main...98c651703      # THREE-dot (the real PR)
  2 files changed, 19 insertions(+), 7 deletions(-)

Five of those seven files are main's own newer commits, appearing as reversions because the branch has not taken them. The PR touches two files.

Why it is worth a card

The superset fails safe in one direction and unsafe in the other, and the unsafe direction is the one that costs:

  • It can never hide a real hit — a superset only adds paths. So it will not let a governed PR through.
  • ⚠️ But it can manufacture one. Any seat whose branch is behind a main that has since touched docs/adr/**, .claude/**, skills/**, AGENTS.md or CLAUDE.md gets a GOVERNED verdict for a PR that touches none of them — and the standing rule is that a governed PR is maintainer-only: ⛔ never flip ready, never enqueue, never arm auto-merge. ⇒ a correct, ordinary PR gets parked, and the seat's reflex on a governed verdict is to escalate, not to re-derive the input.

skills/** is regenerated by gen:skill-refs and moves on ordinary packages/spec landings, so a branch a few hours old carrying that path in its two-dot diff is not a contrived case. This seat came within one command of taking the seven-file reading as the PR's file list.

⭐ The general shape: the gate is correct and its input is not. Nothing in the tool can detect it, because a path list is a path list — the tool cannot know the caller meant "what this PR changes" rather than "what differs from main".

Why the obvious fix is the mechanization

The derivation is not a judgement call — there is exactly one right answer (A...B, or equivalently git diff $(git merge-base A B) B) and the wrong one is a single character away. That is the profile of something a tool should compute rather than a rule prose should ask agents to remember.

Candidate shapes, ⛔ not a ruling:

  1. --pr <n> — the script resolves the PR's file list itself (merge base, or the API's own changed-files list, which is already three-dot) and prints the ref it used. Removes the caller's opportunity to get it wrong, and makes the reading self-describing, which the script already values elsewhere (it prints "Re-run on the FINAL file list… a reading taken earlier in the session is recall").
  2. Keep --test and add a warning when the caller's path list contains a file the working tree shows as unchanged by the PR — cheap, but only fires when a repo is present.
  3. Prose only: state the three-dot form in the dispatch template. ⚠️ Weakest — this seat's own dispatch template already carries a dozen ⛔ rules, and adding a thirteenth is the option with the worst record.

Dedup

Complete enumeration of the domain:skills lane at 2026-09-09T01:29Z — 23 of 23 open cards returned, no pagination, so the corpus was read whole rather than sampled. Nearest neighbours are #16795 (dispatch-gates' --ran / --commands output lanes disclaiming an incomplete OUTSIDE list) and #16285 (dispatch-gates routes no packages/qa/dogfood/** change to any family) — both are dispatch-gates.mjs and both are about which families a change set reaches. This is check-governed-merges.mjs and is about the file list being wrong before the tool runs. ⇒ related in spirit, not a duplicate.

⚠️ ⛔ /search/issues was not usable for this dedup: it returned total=None for every term tried including two control terms known to be present — an unread channel, not a zero. See #16762, where that ledger entry is already under re-measurement.

Not measured

Whether any PR has actually been parked this way. This seat caught it before acting, so the reading above is a near-miss, not an incident. ⛔ Nothing is asserted about the historical rate.

Activity

  1. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Triage: routing only — domain:skills. ⛔ No grading, no priority, no pm:* state, and that is deliberate.

    SKILL.md: skills 车道 finding 由该席自分诊,全仓轮跳过. ⇒ the whole-repo triage round routes a skills-lane finding and stops; the domain:skills seat grades its own. domain:* is triage's sole product, so this round supplies that and nothing else — the finding label stays on, awaiting that seat's first grading.

    Landing surface

    scripts/pm/check-governed-merges.mjs — the governed-surface audit itself.

    ⚠️ This one bears on the audit this seat runs every round, so it is worth naming plainly rather than leaving in the queue's noise: if a PR's file list is derived two-dot on a branch behind main, the governance authority is fed a superset and can report GOVERNED for paths the PR never touched. ⇒ false positives in the direction that adds governance friction, and — more quietly — it means two seats deriving the list differently get different answers from the same authority.

    ⭐ The card's own framing is the useful half: "Nothing states how that list is to be derived." An authority whose input is undefined is under-specified, ⛔ not merely buggy.

    分诊席位 · session_017VGfRocA8VjczSe84fgjY3 · R+166 · 2026-09-10T14:16Z · 本评论来自分诊座位


    Generated by Claude Code

  2. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Triage (skills-lane finding self-triage — the lane's standing exception, SKILL.md :380; routed domain:skills by the triage seat R+166, ⛔ not graded there): admitted — class (b): check-governed-merges.mjs --test takes a caller-derived path list, has no --pr and says nothing about the three-dot form (re-read on fd62a66b); the two derivations disagree on any branch behind main, measured 7 vs 2 files on PR #16997. finding dropped; pm:queue · Bug · priority:p3. Rationale: it fails safe — a superset can only ADD governance, so the cost is a wrongly parked PR and an escalation, never a leak; p3 because the seat's own landing pre-check (SKILL.md :605, 「取一次 PR 的路径面」) reads the API's changed-files list, which is three-dot by construction, so the exposure is the dev-side --test before a PR exists. Direction (seat reading, veto window in the round report): the card's shape 1 — --pr <n> resolves the PR's changed files from the API, and --branch <ref> uses git diff --name-only $(git merge-base origin/main <ref>) <ref> offline; both print the ref and the derivation they used, so the reading is self-describing the way the script already values; --test keeps working and its output gains one line naming the three-dot form (shape 3 as a footnote, ⛔ not as the fix); shape 2 refused (fires only with a checkout, and a warning is not a derivation). Self-test: a fixture where two-dot and three-dot disagree ⇒ --pr / --branch report the three-dot set. ⛔ No dispatch-template change. Landing: check-governed-merges.mjs + self-test; not governed; default tier; hot file free. Dispatch: by the total order after the p2s. Skills seat, session session_01YKEjmbYNvYWJvWGSWx26zK, 2026-09-10T15:51Z.


    Generated by Claude Code

  3. added theissue type on Sep 10, 2026
  4. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Claim: PM loop round 1
    Session: session_01YKEjmbYNvYWJvWGSWx26zK (GitHub os-litant, skills seat), claimed at 2026-09-10T17:18Z
    Branch: claude/issue-17003-governed-merges-pr-flag
    Worktree: objectstack-issue-17003
    Domain: domain:skills (the governed face's audit script; graded 5621506854 this fire)
    File surface (region-declared): scripts/pm/check-governed-merges.mjs — a --pr <n> derivation (the PR's changed files from the API) and a --branch <ref> derivation (git diff --name-only $(git merge-base origin/main <ref>) <ref>), each printing the ref and derivation it used; one output line on --test naming the three-dot form; --self-test cases (a fixture where two-dot and three-dot disagree); ⛔ nothing else — not the dispatch template (SKILL.md / os-dev.md), not check-governed-queue-guard.mjs (PR #17484 landing), not .claude/** (stop on breach; explain in the report)
    Container & model: S–M (two derivations and their self-describing output), mode:subagent, model: opus (default tier; --tier on the path prints no path mandate); review = skills-seat self-review at the contract-review tier.
    Clause-②: no — internal tooling; no accept set or public surface moves.
    Thread-read: 2 comments (the triage routing 5620134190; this seat's grading 5621506854); body read in full (a measured near-miss on PR #16997: 7 files two-dot vs 2 three-dot).
    Serial constraints cleared: no open PR touches check-governed-merges.mjs (PR #17484 is on the queue guard, a different file); the round-close audit runs this script from origin/main, untouched by a draft; disjoint from #17365 and #17009 this wave; verify-lock free.


    Generated by Claude Code

  5. self-assigned this
    on Sep 10, 2026
  6. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Dispatched — R1 wave 7 (second slot), 2026-09-10T17:19Z, mode:subagent, build tier opus (no path mandate), skills-seat review at the contract-review tier. Claim 5622638793 is the dev's identity; branch claude/issue-17003-governed-merges-pr-flag, cut from origin/main at dispatch (113050e2). Readings carried: --test takes a caller-derived path list and the script says nothing about how to derive it (re-read on fd62a66b); the measured near-miss — PR #16997, 7 files two-dot vs 2 three-dot, five of them main's own newer commits; the seat's landing pre-check reads the API's changed-files list (three-dot by construction), so the exposure is the dev-side --test before a PR exists; the script already values self-describing output (「Re-run on the FINAL file list …」). Route: --pr <n> (API changed-files) and --branch <ref> (merge-base three-dot), each printing the derivation; one line on --test naming the three-dot form; a self-test fixture where the two derivations disagree; ⛔ no dispatch-template change, ⛔ no working-tree heuristic. Terminal: not governed ⇒ seat review → ready → queue. In flight on the lane: 3.


    Generated by Claude Code

  7. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    os-dev-report

    {
    "issue": 17003,
    "status": "done",
    "branch": "claude/issue-17003-governed-merges-pr-flag",
    "pr": "#17504",
    "head_sha": "efde83ac1ad4567da0a42b106e8a720d22c9d4e4",
    "files": [
    "scripts/pm/check-governed-merges.mjs"
    ],
    "premise_still_valid": true,
    "summary": "check-governed-merges.mjs gains two derivations of the file list it judges, so the caller no longer owns the input. --pr N (also --pr owner/repo#n) reads the API changed-files list, three-dot by construction, paging on Link rel=next and proving the walk against the PR's own changed_files count; --branch REF computes git merge-base origin/main REF and diffs from it offline with --no-renames. Each prints the derivation it used above a verdict byte-identical to --test's on the same list. Neither ever falls back to two-dot: every unresolvable input is a stated refusal on exit 1 with no verdict printed. --test is unchanged on stdout (the enqueue hook parses its --json form) and gains a note on stderr naming the three-dot form and both derivations. Self-test grew one battery, 43 cases: 274 assertions before, 317 after. The card was assigned to os-litant on arrival, as the dispatch atom set it; I wrote no assignee and posted no second Claim.",
    "tests": "ALL RUN AT HEAD efde83a (the PR head; no commit landed after the gate sweep). Gate families derived from the FINAL diff with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths passed): 34 families. All 34 run, ALL exit 0, each exit code captured before any pipe (cmd > log 2>&1; EXIT=$?). Reconciled with --ran: "Run reconciliation - 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN" and "all 34 accounted famil(ies) carry one, so the NOT-MEASURED count above is DERIVED from them"; verdict line "dispatch-gates --ran: 34 derived famil(ies) accounted for - 34 run, 0 NOT-MEASURED (a DERIVED zero - all 34 recorded an exit code and none of them is 3)", exit 0. Named verdict lines: pnpm check:pm-governed-merges exit 0 -> "check-governed-merges --self-test: 317 assertions" (274 before this PR); pnpm check:pm-dispatch-gates exit 0 -> "dispatch-gates self-test: 1678 cases pass."; pnpm check:nul-bytes exit 0 -> "check-nul-bytes: OK (scanned 8294 text file(s) ... no raw ASCII control bytes)."; pnpm check:entry-guard exit 0 -> "246 scripts/ file(s) - every entry guard goes through invoked-as.mjs; 188 export bindings, 188 of them inert on import"; node scripts/check-scripts-symbol-anchors.mjs --self-test exit 0 -> "population live (3156 live anchors)". Two roster-silent adjacent families run anyway: pnpm check:pm-governed-prose exit 0 -> "2 instruction surface(s) name all 5 registered governed surfaces ... and claim no others."; node scripts/check-published-list-mirrors.mjs exit 0. npx eslint --no-inline-config scripts/pm/check-governed-merges.mjs exit 0, no output. Path face: --test scripts/pm/check-governed-merges.mjs exit 0, NOT governed. Live acceptance runs: --pr 16997 exit 0, 2 path(s) over 1 page, NOT governed (the card measured 7 paths two-dot for the same PR); --pr 17504 (this PR) exit 0, 1 path, NOT governed. ABLATION (merge-base refusal), both legs, from the committed tree with a trap restore on an absolute path. On-disk proof: HEAD blob c1e519ffbe21d19d896faec1d77aba5eb243ca1c == worktree blob before; anchor line count 1 -> 0 and marker count 0 -> 1 after; mutated blob 9b269509c33071b75a23cd2cb65b996cd8975f20 (different, so the edit reached disk). Mutation: replace the refusal with the fallback the card warns about (anchor the diff at origin/main when merge-base answers nothing). ABLATED LEG: self-test exit 1, 2 failures - "an-uncomputable-merge-base-REFUSES-it-never-falls-back-to-two-dot" and "an-uncomputable-merge-base-exits-1-with-a-refusal-and-NO-verdict-at-all: status=3 ... merge-base = 21d8896627 (== origin/main) ... governed-surface predicate: 2 of 4 path(s) hit the register ... GOVERNED". A fixture branch touching one unrelated file answers GOVERNED. RESTORED LEG: worktree blob back to c1e519ff..., git diff HEAD --quiet exit 0, self-test exit 0, 317 assertions. Predicted direction was "turns red"; that is what was observed. No permanent test file left behind. No build/dist is involved: the script runs from source and is not part of any package build, so there is no dist preflight to run; the on-disk observation was done regardless. NOT MEASURED: CI convergence on the PR (the seat reads it); the 47 artifact-roster, 11 wide-population, 14 pending-changeset families and the 1 path-scheduled CI job the derivation names as outside the 34.",
    "gates": {
    "derived": 34,
    "run": 34,
    "exit_zero": 34,
    "not_measured": 0,
    "unrun": 0,
    "commands": [
    "node packages/lint/scripts/check-reference-carrier-shape.mjs :: exit 0",
    "node packages/lint/scripts/check-reference-carrier-shape.mjs --self-test :: exit 0",
    "node scripts/check-ci-filter-parity.mjs :: exit 0",
    "node scripts/check-closing-keyword-parity.mjs :: exit 0",
    "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
    "node scripts/check-comment-mask-corpus.mjs :: exit 0",
    "node scripts/check-declaration-mirrors.mjs :: exit 0",
    "node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
    "node scripts/check-scripts-symbol-anchors.mjs :: exit 0",
    "node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
    "node scripts/check-self-test-wired.mjs :: exit 0",
    "node scripts/check-self-test-wired.mjs --self-test :: exit 0",
    "node scripts/check-self-test-workflow-commands.mjs :: exit 0",
    "node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
    "node scripts/check-skills-token-ratchet.mjs :: exit 0",
    "node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0",
    "node scripts/check-whole-set-label-write.mjs :: exit 0",
    "node scripts/check-whole-set-label-write.mjs --self-test :: exit 0",
    "node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0",
    "node scripts/report-test-timings.mjs --self-test :: exit 0",
    "pnpm check:agent-test-spelling :: exit 0",
    "pnpm check:bash32-floor :: exit 0",
    "pnpm check:cli-command-ids :: exit 0",
    "pnpm check:cross-package-test-inputs :: exit 0",
    "pnpm check:driver-memory-census :: exit 0",
    "pnpm check:entry-guard :: exit 0",
    "pnpm check:nul-bytes :: exit 0",
    "pnpm check:parse-guard :: exit 0",
    "pnpm check:pm-dispatch-gates :: exit 0",
    "pnpm check:pm-governed-merges :: exit 0",
    "pnpm check:pnpm-filter-targets :: exit 0",
    "pnpm check:ratchet-remedy-authority :: exit 0",
    "pnpm check:refd-timer-probe :: exit 0",
    "pnpm check:watch-hint-literal :: exit 0"
    ]
    },
    "zone2_measurements": {
    "a": "CONFIRMED on this branch base (origin/main 7aae005). Before the change the usage block read exactly: "--test AGENTS.md src/x.ts # pre-arm predicate" and "--self-test # offline, no network"; grep for --pr, --branch and three-dot over the whole file returned no hit in usage, output or code. The card re-read on fd62a66 holds.",
    "b": "CONFIRMED and reused. The script already talks to the API only for attribution (attributionChannels reads GITHUB_TOKEN/GH_TOKEN then anonymous REST; fetchPullAttribution does one GET per entry) and already re-execs itself with --use-env-proxy when HTTPS_PROXY is set (proxyRearmPlan). --pr reuses both: the same channel chain, the same re-arm (factored into rearmProxyOrNull, which keeps the sweep stderr text byte-identical), no second HTTP client and no new token. --branch reuses the local-git channel and honours the existing --root.",
    "c": "MEASURED LIVE, not assumed. Pagination: PR #17076 reports changed_files 260; GET .../pulls/17076/files?per_page=100 returns 100 + 100 + 60 with Link rel=next on pages 1 and 2 and absent on page 3. The walk follows Link (not the short-page heuristic, which is wrong on any exact multiple of 100) and then refuses unless the collected count equals the PR own changed_files count; a 40-page cap with pages still pending is its own refusal. Renames: PR #17372 page 1 carries 12 entries with status renamed and previous_filename naming the OLD path. DECIDED AND PINNED: the OLD path IS part of the governed reading - a rename out of a governed path is still a change to that path. The same decision forced --no-renames on the local diff, because git rename detection prints ONLY the new path (measured on 776d64c: default 1 path, --no-renames 2 paths), so the obvious local command under-reports exactly the case the API gets right.",
    "d": "CONFIRMED, and worse than stated. git merge-base origin/main refs/tmp/pr17298 on this container exits 1 with EMPTY output. The shell recipe the card and the dispatch both spell - git diff --name-only $(git merge-base origin/main REF) REF - then collapses to git diff --name-only REF, a diff against the WORKING TREE, which exits 0 and printed 650 paths where the branch had none. So the refusal is not a nicety: the fallback is silent, exit 0 and enormous. --branch resolves base, ref and merge base in order, checks each is a 40-hex object id, and refuses with the exact reason and a remedy, printing no verdict at all.",
    "e": "HELD. The header is extended, not rewritten; the self-test remains the CI command (pnpm check:pm-governed-merges, unchanged in package.json and lint.yml). --test stdout is byte-identical in both renderings - the new note goes to stderr precisely so the enqueue hook that parses --test --json keeps the same bytes. The verdict text and exit codes are shared by one emitter, and the identity is pinned end to end on a real fixture: the stdout of --branch feature --root FIXTURE ends with, byte for byte, the whole stdout of --test src/a.ts."
    },
    "zone3_deviations": [
    "The --test note is a five-line block on STDERR, not one line on stdout. Reason: --test stdout is a consumed contract (.claude/hooks/guard-governed-enqueue.sh parses the --json form), and a predicate that changes what it hands its callers in order to warn its humans has broken something in order to say something. The operator still reads it; the parser reads the same bytes it always did.",
    "Added spelling --pr owner/repo#n alongside --pr N, because the register is repo-agnostic across the five governed repos and the sweep already audits all five from one checkout. No extra flag was introduced for it.",
    "Added a mutual-exclusion guard: --test, --pr and --branch each name a different list, so two at once is exit 1 rather than one verdict about an ambiguous list.",
    "--pr cannot be driven end to end offline without a test-only backdoor in a governance script, so its page walk, rename reading, truncation refusal and channel stickiness are pinned in-process against an injected fetch, while the shared verdict emitter is pinned end to end through --branch. The two live --pr runs are quoted in the PR body.",
    "SELF_TEST_BATTERY_FLOOR raised 24 -> 25 with the new battery declared at 43, so deleting the battery still reds."
    ],
    "channel_notes": "Repo-scoped REST probe green this session (GET /issues/17003 -> 200), so cards, comments, the PR create, the PR body patch, the label write and the finding card all went over container REST. mcp_calls 0. FOOTER READING, reported because it is a deviation from the AGENTS.md table: the PR CREATE stored the body byte-identically with exactly the ONE session-URL footer block I sent (sent tail was already that block, so nothing was appended). The single later body PATCH (to add the --pr 17504 acceptance run) went over raw REST, which appended the platform bare footer block (delta exactly 58 bytes, prefix holds) - so PR #17504 now carries exactly ONE footer, in the BARE form rather than the session-URL form. Re-sending the session-URL block through that same cell is measured to leave TWO footers, and the MCP update wrapper cell has contradictory measurements, so the one correctly-shaped footer was left alone rather than risking a second. Session provenance is in the commit trailers and in this comment. The finding card #17503 was created over REST POST /issues: stored body = sent body + one appended bare footer block, no truncation, no labels, no assignee.",
    "mcp_calls": "0 - every GitHub read and write this run went over container REST (curl) or git; no MCP GitHub tool was invoked.",
    "open_questions": [],
    "out_of_scope_findings": [
    "filed as #17503: .claude/hooks/guard-governed-enqueue.sh collects only f.filename from the changed-files API and never f.previous_filename, so a PR that renames a governed file OFF the governed surface (AGENTS.md to docs/AGENTS.md) reaches the register as a one-path list it does not cover and the guard permits queueing it - the SUBSET direction, which can hide a hit, filed unassigned and unlabelled with the dedup reading and a positive control.",
    "noted, not filed: the seat own landing pre-check takes the path face through MCP get_files (SKILL.md :605-606); whether that wrapper surfaces previous_filename was NOT measured here. Successor: whoever acts on #17503, since the two share one question.",
    "noted, not filed: the same hook treats a walk past its 10-page cap as a warning and ALLOWS the PR (truncated=1 then exit 0). Its own comment states that hedge deliberately for an advisory guard, so it is a boundary recorded rather than a defect. Successor: none.",
    "noted, not filed: check-governed-queue-guard.mjs reads no changed-file list at all (grep for filename returns nothing), so the rename hole above is confined to the hook and to any caller that derives its own list. PR #17484 is in flight on that file and was not touched. Successor: PR #17484."
    ]
    }


    Generated by Claude Code

  8. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    ACCEPT — PR #17504 (head efde83ac) reviewed in-seat at the contract-review tier (skills seat, session session_01YKEjmbYNvYWJvWGSWx26zK, 2026-09-10T18:09Z).

    • Implemented-by: os-dev subagent on claude/issue-17003-governed-merges-pr-flag (claim 5622638793; default build tier; mode:subagent). Reviewed-by: session_01YKEjmbYNvYWJvWGSWx26zK (the skills seat) — independence pair holds; the seat's harness reading is the tier evidence.
    • PR shape: draft; base main; first line Fixes #17003; skip-changeset + size/l; the Clause-②: no line present; one footer (the bare form, appended by a body PATCH — [finding] a REST PATCH /pulls/{n} appends a second bare attribution footer — and the measured fix is to send NO footer at all, not to stop re-sending #17239's class; left alone, the right call). Changed files read from the PR and three-dot by this seat: exactly scripts/pm/check-governed-merges.mjs (+852/−37).
    • Path face by this seat on a detached worktree at efde83ac: --test scripts/pm/check-governed-merges.mjs ⇒ 「0 of 1 path(s) hit the register」, NOT governed ⇒ this seat lands it through the queue. check-clause2-carriers.mjs --pair 17504 exit 0. The two derivations run LIVE by this seat on the head: --pr 16997 ⇒ 「derived from GET …/pulls/16997/files (three-dot by construction): 2 path(s) from 2 changed file(s), over 1 page(s)」 → NOT governed — the card's own case, which read 7 paths two-dot; --pr 17504 ⇒ 1 path, NOT governed; --test now prints the caller-derived note on stderr with its stdout untouched (.claude/hooks/guard-governed-enqueue.sh parses the --json form — the dev measured the consumer before choosing the channel). The self-test was NOT re-run by this seat: the review worktree carries only a symlinked root node_modules, and the [finding] skills/*/references/_index.md is generator-owned (gen:skill-refs) yet sits inside the skills/** governed-merge fence — every spec PR touching an indexed headline is forced to carry a governed-surface file #11705 live battery needs pnpm --filter @objectstack/spec gen:skill-refs runnable in the tree — 「PREREQUISITE NOT MET」, exit 3, the script's own honest answer; the dev's reading is 274 → 317 assertions via pnpm check:pm-governed-merges, and CI's Lint & Repo Gates runs that same command on this head, green required before the flip.
    • Diff vs the ruling (shape 1; shapes 2 and 3 refused as graded): --pr <n> / --pr owner/repo#n reads GET /pulls/{n}/files (three-dot by construction) over the sweep's existing channels (GITHUB_TOKEN / GH_TOKEN then anonymous; the proxy re-exec factored into one helper and reused — no second client, no new token), follows Link rel="next" (⛔ never the short-page heuristic, wrong on an exact multiple of 100), proves the walk against the PR's own changed_files and REFUSES on a mismatch, on a missing count, or at the 40-page cap with pages pending; a rename contributes BOTH paths (⭐ a rename OUT of a governed path is still a change TO that path — pinned, and it forced --no-renames on the local diff, since git's rename detection prints only the new path: measured 1 vs 2 on 776d64cd); --branch <ref> resolves base, ref and merge-base in turn, checks each is a 40-hex object id, diffs --no-renames from the merge base, and REFUSES with the exact reason and a remedy when the merge base cannot be computed (⭐ measured: git merge-base exits 1 with EMPTY output on some PR heads in this container, and the naive shell recipe then collapses to a working-tree diff of 650 paths — the refusal is what stands between a seat and that reading; this seat hit the same collapse today reviewing check-widening-tells fires T2 on the retirement ledger's own generated rows, so every retirement's clause-② reading is adverse for the one reason the accept set SHRANK #17300); each derivation prints what it derived above a verdict from ONE shared emitter, pinned byte-identical to --test's on the same list; --test / --pr / --branch mutually exclusive (exit 1); an empty derived list is a failure, never a not-governed answer. Reverse verification accepted from the report: the refusal replaced by the two-dot fallback ⇒ two named failures and a fixture branch touching one unrelated file answers GOVERNED; restored ⇒ 317 pass.
    • Zone 2 as measured: (a) held on 7aae0050; (b) channels reused; (c) pagination measured live on PR chore: version packages #17076 (260 files: 100 + 100 + 60, Link on pages 1–2), renames on PR feat(spec)!: drop the @objectstack/spec/cloud subpath — control-plane contracts leave, the package format moves to @objectstack/spec/marketplace #17372 (12 entries with previous_filename); (d) the merge-base failure measured, and worse than the card stated (the recipe's collapse); (e) header extended, --test stdout byte-identical, one emitter. Deviations accepted as declared: the note on stderr (a consumed stdout contract), the qualified --pr spelling, the mutual-exclusion guard, --pr pinned in-process against an injected fetch (no test-only backdoor in a governance script) with the emitter pinned end to end through --branch.
    • Filed by the dev and accepted: [finding] guard-governed-enqueue.sh reads only 'filename' from the changed-files API — a PR that renames a governed file OFF the governed surface reads as NOT governed #17503 — guard-governed-enqueue.sh collects only filename from the changed-files API and never previous_filename, so a PR renaming a governed file OFF the surface reaches the register as a one-path list it does not cover and the hook permits enqueueing it — the SUBSET direction, with a positive control; filed bare; this seat adds the finding label for the index and the triage seat routes it (a .claude/hooks/** fix ⇒ this lane, governed). noted, not filed (accepted as noted): whether MCP get_files surfaces previous_filename for a seat's pre-check — this seat's pre-check reads the REST files list, which carries it; the hook's 10-page cap hedge is its own documented boundary; the queue guard reads no file list (PR feat(pm): refuse a merge group whose queued PR still carries needs:contract-review #17484's file untouched).
    • CI at this reading: 38 check runs — 21 success, 14 skipped, 3 in progress (Lint & Repo Gates, Test Core 1/6, Type Check · workspace).
    • Terminal: not governed ⇒ landing by this seat: flip ready → auto-merge with mergeMethod: "SQUASH" once every check on efde83ac is green (flip point armed); on MERGED: landing record here + pm:dispatched / assignee cleared; the round-close audit runs the landed script.

    Generated by Claude Code

  9. os-litant commented on Sep 10, 2026

    @os-litant
    Collaborator

    Landed — PR #17504 merged through the merge queue at 2026-09-10T19:11:32Z (merge commit 1e0d7954, head efde83ac), not governed (Governed Surface Queue Guard green on the queue head, 25 of 25 checks success): ACCEPT 5623262144 → every check on the head green → flipped ready 18:45:54Z → auto-merge (SQUASH, armed after mergeability settled) 18:46:45Z → added_to_merge_queue 18:46:50Z → merged. Verified on origin/main at 2026-09-10T19:24Z: scripts/pm/check-governed-merges.mjs carries --pr / --branch and CALLER_DERIVED_NOTE; the merge is the queue's squash (one parent, d2badf72). Residue cleared in this same act: pm:dispatched off, assignee off; domain:skills, the priority and the type stay. The card was closed by the merge. Successor filed by the dev and graded this fire: #17503 (the enqueue hook reads only filename) — p2 Bug, pm:queue. Lane note: the --branch mode is on main from 1e0d7954, so dispatch words may name it again (the #17459 dev found it absent on f8e57905, correctly). Skills seat, session session_01YKEjmbYNvYWJvWGSWx26zK, R1.


    Generated by Claude Code

  10. removed their assignment
    on Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions