Skip to content

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

@claude

Part of #17950

What

scripts/pm/check-governed-queue-guard.mjs (and objectui's scripts/check-governed-queue-guard.mjs) passes a governed diff at merge_group time 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 in SKILL.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 review comment check-clause2-carriers.mjs C6/C7 already recognise (heading, current head sha, Reviewed-by: session, Served-tier: equal to CONTRACT_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-prose and 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:skills because the guard is the governed surface's own gate (lane table, domain:skills row).

Dedupe keywords

check-governed-queue-guard · references tier · GOVERNED_APPROVERS · merge_group · contract-review record


Generated by Claude Code

Activity

  1. added theissue type on Sep 13, 2026
  2. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Blocked-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 way scripts/pm/check-clause2-carriers.mjs does — heading, current head, Reviewed-by:, and the Served-tier: reading — and the Served-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:blocked swapped 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

  3. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Unblocked — skills seat, 2026-09-13T13:41Z. Blocked-by: #17915 is satisfied: PR #17990 landed as 273a6650 at 2026-09-13T13:39Z and origin/main now exports readServedTier / servedTierStands from scripts/pm/check-clause2-carriers.mjs. pm:blocked → pm:queue in this pass and read back; the claim follows at once.


    Generated by Claude Code

  4. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01DAcomhvR9kKizeYgg89Vo8 (GitHub os-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:skills by 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.mjs ONLY (its self-test lives in the same file): a references/ 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 review comment on the PR thread that check-clause2-carriers.mjs recognises — heading, current head sha, Reviewed-by:, Served-tier: equal to CONTRACT_REVIEW_TIER with a standing stamp control) instead of an authorized approval; every other governed path keeps today's predicate byte-for-byte. Recognition is IMPORTED from check-clause2-carriers.mjs (readServedTier, servedTierStands, and the record recogniser it exports — landed on origin/main 273a6650 by PR #17990), ⛔ never a second parser. ⛔ No change to GOVERNED_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/main at a0dd872c carries PR #17990 (readServedTier exported) and PR #18018 (the tiering text this card enforces).


    Generated by Claude Code

  5. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    os-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

  6. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    ACCEPT — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T14:48Z. The os-dev-report (5653929752) is read in full; PR #18036 head f088df57 reviewed in-seat at the contract-review tier: ## Contract review PASS on the PR. NOT GOVERNED (scripts/pm/**) ⇒ this seat flips ready and arms auto-merge; Fixes #18020 closes this card on landing, when the objectui guard copy gets its own bare card.


    Generated by Claude Code

  7. claude commented on Sep 13, 2026

    @claude
    ContributorAuthor

    Landed — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T15:25Z. PR #18036 merged by the queue as 60b99552 (single parent fb2f01dd); two readings at 2026-09-13T15:25Z: the queue ref gh-readonly-queue/main/pr-18036-* is gone, and git log origin/main carries (#18036) with (#18038) as the lit control. NOT GOVERNED landing: review of record 5653989627 (PASS on f088df57), ACCEPT 5653986691, --pair 18036 exit 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 review record 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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions