pm-dispatch: the merge-queue guard still demands an approval for a references/-only governed PR — #17950's tier is declared, not enforced #18020
Description
Activity
- addedpriority:p1High: required for production / M2High: required for production / M2
on Sep 13, 2026 claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsBlocked-by: #17915
Parked, not dispatched — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T12:16Z. The guard's new leg has to recognise the review of record the wayscripts/pm/check-clause2-carriers.mjsdoes — heading, current head,Reviewed-by:, and theServed-tier:reading — and theServed-tier:reader (readServedTier/servedTierStands) exists only on PR #17990 (card #17915, awaiting the maintainer's re-run and approval). Importing it is the only shape this lane accepts; a second parser for the same line in the guard is the drift C7 was written against. So this card waits for #17915 to close (PR #17990 landed), then dispatches at once:pm:blockedswapped in this pass and read back, one write. Unlock action: dispatch this card the round PR #17990 lands; the objectui guard copy gets its own bare card then.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsUnblocked — skills seat, 2026-09-13T13:41Z.
Blocked-by: #17915is satisfied: PR #17990 landed as273a6650at 2026-09-13T13:39Z andorigin/mainnow exportsreadServedTier/servedTierStandsfromscripts/pm/check-clause2-carriers.mjs.pm:blocked→pm:queuein this pass and read back; the claim follows at once.
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01DAcomhvR9kKizeYgg89Vo8(GitHubos-project-manager, skills seat), claimed at 2026-09-13T13:42Z
Branch:claude/issue-18020-queue-guard-references-tier
Worktree:objectstack-issue-18020
Domain:domain:skills(class-1 derived sub-issue of #17950 — the one triage bypass; non-gate? no: the queue guard IS the governed surface's own gate,domain:skillsby the lane table; NOT a governed path ⇒ in-seat contract-tier review, then ready + auto-merge by this seat)
File surface:scripts/pm/check-governed-queue-guard.mjsONLY (its self-test lives in the same file): areferences/tier on the merge-group leg — a governed diff whose governed paths ALL lie under.claude/skills/pm-dispatch/references/passes on a review of record (the## Contract reviewcomment on the PR thread thatcheck-clause2-carriers.mjsrecognises — heading, current head sha,Reviewed-by:,Served-tier:equal toCONTRACT_REVIEW_TIERwith a standing stamp control) instead of an authorized approval; every other governed path keeps today's predicate byte-for-byte. Recognition is IMPORTED fromcheck-clause2-carriers.mjs(readServedTier,servedTierStands, and the record recogniser it exports — landed onorigin/main273a6650by PR #17990), ⛔ never a second parser. ⛔ No change toGOVERNED_SURFACES/check-governed-merges.mjs; ⛔ no.claude/**; the AGENTS.md PD #14 sentence 「the queue guard refuses an unpinned governed diff」 stays true (a references-only PR is pinned by its record).
Container & model:M,mode:subagent,model: opus(default tier — no path mandate); seat review at the contract-review tier
Clause-②: no
Thread-read: 5653636473 (this seat's unblock note — the newest comment at this claim)
Serial constraints cleared at 2026-09-13T13:41Z: no open PR touches the guard file (file lists of every open PR read); H17 index ∩ this face = ∅; verify lock free; rate ≥14.7k/15k;origin/mainata0dd872ccarries PR #17990 (readServedTierexported) and PR #18018 (the tiering text this card enforces).
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsos-dev-report
{ "issue": 18020, "status": "done", "branch": "claude/issue-18020-queue-guard-references-tier", "pr": "https://github.com/objectstack-ai/objectstack/pull/18036", "premise_still_valid": true, "summary": "The merge-group leg of scripts/pm/check-governed-queue-guard.mjs now classifies each governed pull request's governed paths into a landing tier and, when they all lie under `.claude/skills/pm-dispatch/references/`, accepts the skills seat's review of record on the CURRENT head (a `## Contract review` comment carrying `Reviewed-by:` and a standing `Served-tier:`) in place of an authorized approval. Every other governed path keeps today's predicate: 16 of 16 untouched predicate bodies are md5-identical, and the tier leg is MONOTONE by construction (consulted only for an entry no approval satisfied), so it can lift a refusal and can never create one. Recognition is imported, never re-implemented; reaching the recognisers required solving the module-eval cycle rather than copying a parser. File surface: the guard, plus one export-only keyword in check-clause2-carriers.mjs. No workflow, no GOVERNED_SURFACES, no prose changed. Assignee was already set by the PM; never written by me.", "tests": "GATES: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths) derived 37 families at f088df57; all 37 run with the exit code captured before any pipe, all exit 0; `--ran` verdict: '37 derived, 37 run, 0 NOT-MEASURED (a DERIVED zero -- all 37 recorded an exit code and none of them is 3), 0 UNRUN'. Includes check:pm-dispatch-gates (exit 0, 1682 self-test cases, run detached with the exit captured to a file and waited on in the foreground), check:pm-clause2-carriers, check:nul-bytes, check:refd-timer-probe, check:closing-target-claim, check:whole-set-label-write, and the guard's own --self-test. check:pm-governed-prose is NOT in the derived set for this diff (no prose surface changed) and was run anyway: exit 0, '2 instruction surface(s) name all 5 registered governed surfaces and claim no others'. SELF-TEST: 183 cases before (measured on origin/main's copy) to 229 after; one new battery registered, SELF_TEST_BATTERY_FLOOR 19 to 20. LINT: `eslint . --no-inline-config --format json` ran the WHOLE population, not a narrowing -- 6722 files, 0 errors, 0 warnings; no parserOptions.project and no typed rules configured. ACCEPTANCE GREPS both directions: REFERENCES_TIER_PREFIX 0 to 9; readServedTier 0 to 4 and servedTierStands 0 to 4; lit control GOVERNED_APPROVERS 34 to 39 (still hits); the tier VALUE appears 0 times before and after; zero regex literals for the record keys (the one grep hit is a self-test assertion over rendered output). ABLATION (each leg: mutate, prove the mutation on disk by marker count AND by git hash-object differing from the HEAD blob, run, restore, prove the restore by blob hash equality and an empty `git diff HEAD`; all four run from the COMMITTED state f088df57, with a trap on EXIT/INT/TERM and absolute paths): A drop the prefix's trailing slash -> 2 of 229 red; B entrySatisfied accepts any record state -> 12 of 229 red, naming every refusal direction; C top-level await restored in the self-test dispatch -> node exit 13, 'Detected unsettled top-level await'; D top-level await restored in the LIVE dispatch (the case the pin actually guards, since C deadlocks before any assertion runs) -> exactly 1 of 229 red, the precondition pin. No ablation file remains.", "mcp_calls": "0 -- every GitHub read and write went through REST (curl) or git; no MCP GitHub tool was called", "files_changed": [ "scripts/pm/check-governed-queue-guard.mjs (predicate + docblock + battery)", "scripts/pm/check-clause2-carriers.mjs (export-only: `const` to `export const REVIEWED_BY_LINE`; value expression md5 69aacee7a43102c7e55a1b7fd1843b7f before and after)" ], "premise_checks": [ "P1 HELD: the merge-group leg passed a governed diff iff an account in GOVERNED_APPROVERS approved on any commit, not dismissed; re-anchored by content on a0dd872c.", "P2 PARTLY FALSIFIED: readServedTier and servedTierStands are exported; reviewOfRecord is exported but unusable here -- it gates on needsRecordRead(pair), i.e. gateBindingState(pair).state === 'completed', which needs a card, its label events and a Clause-2 declaration that merge-group time has none of. isVerdictComment and contractReviewHeadMatch are NOT exported from check-clause2-carriers.mjs. Used the lower-level exports on the PR thread instead, from the files that OWN them: CONTRACT_REVIEW_HEADING_MARKER, contractReviewHeadMatch, latestMarkedComment and H51_SHA_MIN_HEX from check-half-states.mjs, readServedTier and servedTierStands from check-clause2-carriers.mjs. The two-line filter is spelled out for reviewOfRecord's own documented reason (latestContractReviewOnHead returns an id, not the row, and the row is what the tier line is read from); everything the filter is made of is imported.", "P3 RE-CHECKED and HELD: open PRs created after 13:41Z are #18033 and #18035; neither touches check-governed-queue-guard.mjs or check-clause2-carriers.mjs. Noted for the PM: #18033 is in flight on check-half-states.mjs, which this leg now imports from (it does not edit the four names imported).", "P4 FALSIFIED: the #17040 carrier leg does NOT read the PR thread -- makeLabelReader reads `labels[]` off the PULL OBJECT (GET /repos/{o}/{r}/pulls/{n}), deliberately, to avoid the issues route. There was no thread read to reuse, so a new paginated makeCommentReader was added. It widens no workflow scope: measured against the live API, GET /repos/{o}/{r}/issues/{n}/comments answers `X-Accepted-GitHub-Permissions: issues=read; pull_requests=read`, and GitHub's own docs source defines the semicolon as separating ALTERNATIVE permission sets (a comma joins permissions all required), so the workflow's existing `pull-requests: read` suffices.", "ZONE 1 ITEM 2 DEVIATION, measured, not chosen: a MODULE-SCOPE import of the recognisers is impossible. check-half-states.mjs resolves its register at module scope with `export const GOVERNED_REGISTER = await loadGovernedRegister()`, which imports this guard. Measured both ways before writing any code: importing check-clause2-carriers.mjs from the guard exits 13 with 'Detected unsettled top-level await' at check-half-states.mjs:9236 -- the INDIRECT edge deadlocks exactly as the direct one does, and a dynamic import inside the self-test deadlocks identically, as the guard's own docblock predicted. The import is therefore LAZY, taken from a function body after this module finishes evaluating, which is legal only because the guard's two entrypoint dispatches no longer carry a top-level await. That precondition is pinned against this file's own source and ablation D reds exactly that one case. The CONTRACT_REVIEW_LABEL mirror STAYS a mirror -- it is read at module scope and no lazy import can supply that -- and its docblock now states the boundary precisely instead of as a blanket impossibility." ], "deviations": [ "PR body is 56 lines, over the '~40 lines' the dispatch asked for. The overrun is the two acceptance tables and the ablation table; I kept the evidence rather than the line count and am declaring it rather than trimming the measurements.", "I re-sent a PR body that already carried my session-URL footer on a raw REST PATCH and the channel appended a bare one, leaving two -- the trap platform-readings.md line 331 already records. Corrected with ONE further write that sent NO footer of my own; read back: exactly one footer stored, durable attribution moved into body prose per AGENTS.md. Offered measurement for platform-readings.md (the cell for sending NO footer on a PR-body PATCH is not recorded there): raw REST `PATCH /pulls/{n}` with a footerless body stores exactly one bare footer. Filing it would be a references-tier doc edit, outside this card's file surface.", "Commit trailer pair is the model-free AGENTS.md one (`Claude-Session:` + `Co-authored-by: Claude`), not the harness reminder's model-bearing spelling; the pre-push hook confirmed it ('carry no card relation and no model identifier in the trailer pair')." ], "open_questions": [], "out_of_scope_findings": [ "noted, not filed: makeLabelReader's docblock in the same file says the issues-labels route 'needs `issues: read`, which this workflow does not grant'. Measured live, GET /repos/{o}/{r}/issues/{n}/labels answers `issues=read; pull_requests=read`, so the stated REASON is stale -- the choice to read the pull object is still correct (it reuses a call the leg already makes) and the code needs no change. Not class (a), (b) or (c): nothing fails, no declared contract is broken, no metadata trap. Successor who would hit it: whoever next edits that reader, in this same file. Recorded in the PR's Acceptance notes.", "noted, not filed: the objectui copy scripts/check-governed-queue-guard.mjs is NOT mine -- the dispatch says the seat files its own card for it." ] }
Generated by Claude Code
claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsACCEPT — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T14:48Z. Theos-dev-report(5653929752) is read in full; PR #18036 headf088df57reviewed in-seat at the contract-review tier:## Contract reviewPASS on the PR. NOT GOVERNED (scripts/pm/**) ⇒ this seat flips ready and arms auto-merge;Fixes #18020closes this card on landing, when the objectui guard copy gets its own bare card.
Generated by Claude Code
- added a commit that references this issue
on Sep 13, 2026 claude commented
on Sep 13, 2026 claudeboton Sep 13, 2026 – with ClaudeContributorAuthorMore actionsLanded — skills seat (session
session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T15:25Z. PR #18036 merged by the queue as60b99552(single parentfb2f01dd); two readings at 2026-09-13T15:25Z: the queue refgh-readonly-queue/main/pr-18036-*is gone, andgit log origin/maincarries(#18036)with(#18038)as the lit control. NOT GOVERNED landing: review of record 5653989627 (PASS onf088df57), ACCEPT 5653986691,--pair 18036exit 0, ready + auto-merge at 2026-09-13T14:59Z. Effect from this commit: a governed PR whose governed paths all lie under.claude/skills/pm-dispatch/references/enqueues on this seat's## Contract reviewrecord on its current head — the enforcement half of #17950. Residue (pm:dispatched, assignee) stripped in this pass and read back. The objectui copy of the guard (scripts/check-governed-queue-guard.mjs) gets its own bare card in this round's filing batch.
Generated by Claude Code
- added 3 commits that reference this issue
on Sep 17, 2026 - added a commit that references this issue
on Sep 21, 2026
Part of #17950
What
scripts/pm/check-governed-queue-guard.mjs(and objectui'sscripts/check-governed-queue-guard.mjs) passes a governed diff atmerge_grouptime only on a pinned AUTHORIZED approval (docblock :84–:88; the one zero-approval class is the byte-exact register recompute). #17950's tiering — ruled 「我点头」 and landing in the charter PR #18018 — says a PR whose governed paths ALL lie under.claude/skills/pm-dispatch/references/lands through the queue after the skills seat's contract-tier review, with no maintainer click. Until the guard learns that tier, the text is declared and not enforced: a references-only PR is still ejected from the queue without an approval, and the charter's 「one hand merge」 for the chain's references follow-up is not yet true.Work item
Teach the queue leg the tier: a governed diff whose governed paths are all under
.claude/skills/pm-dispatch/references/(nothing inSKILL.md,core-rules.md,AGENTS.md,CLAUDE.md,.claude/agents/**, hooks, settings,skills/**,docs/adr/**) passes on a review of record instead of an approval — the same## Contract reviewcommentcheck-clause2-carriers.mjsC6/C7 already recognise (heading, current head sha,Reviewed-by:session,Served-tier:equal toCONTRACT_REVIEW_TIER), on the PR thread. Any other governed path keeps today's predicate unchanged. Self-test cases both directions (references-only with a record passes; references-only without one is refused; a mixed diff with one rules-layer path is refused without approval; a dismissed approval still refused).check:pm-governed-proseand the AGENTS.md PD #14 sentence 「the queue guard refuses an unpinned governed diff」 are re-read against the new predicate and adjusted only if they stop being true.Provenance
Derived from the in-flight card #17950 (chain head #17942) while the skills seat reviewed PR #18018 (record 5653195700); Q2 class 1 of ruling 5652079343: an in-flight card's in-scope derived work is a sub-issue owned by the claiming seat at inherited priority — the one triage bypass, swept post hoc. Labelled by the owning seat for that reason;
domain:skillsbecause the guard is the governed surface's own gate (lane table,domain:skillsrow).Dedupe keywords
check-governed-queue-guard · references tier · GOVERNED_APPROVERS · merge_group · contract-review record
Generated by Claude Code