Skip to content

Mirror lock 1's enqueue class into objectui's .claude/settings.json: deny mcp__github__enable_pr_auto_merge and mcp__github__disable_pr_auto_merge (objectstack PR #18317, ruling A on objectstack#18282) #9586

Description

@os-zhuang

Filed by the domain:skills execution PM seat at objectstack (session session_01HZfg2AwVX191qCizp88gQr), 2026-09-16T02:19Z, as the cross-repo successor the ruling on objectstack#18282 names: 「the objectui mirror of lock 1 gets its own card after the landing」. objectstack PR #18317 landed as ceb6b5fb46ab at 2026-09-16T02:16:44Z (APPROVED by os-zhuang, ruling C).

Provenance

Measured on objectui origin/main at filing

.claude/settings.json permissions.deny carries 14 mcp__github__* entries (the content-write class objectui PR #9448 ported on 2026-09-14) and NONE of mcp__github__update_pull_request, mcp__github__enable_pr_auto_merge, mcp__github__disable_pr_auto_merge — so objectui is missing all THREE of lock 1's later entries (objectstack landed update_pull_request on 2026-09-15 in PR #18276 and the two enqueue tools in PR #18317, both after #9448). scripts/pm/check-settings-deny-roster.mjs does not exist on objectui (404); porting that declared = enforced gate is the taker's call, stated in the report — the card's ask is the three entries.

One executable criterion

On objectui origin/main after the fix: .claude/settings.json permissions.deny contains both mcp__github__enable_pr_auto_merge and mcp__github__disable_pr_auto_merge (read back through GET /contents/.claude/settings.json), and whatever deny-roster gate objectui carries (if scripts/pm/check-settings-deny-roster.mjs exists here, its constant and cases move with it; if not, the entries alone) reads green in lint.yml. Governed rules layer (.claude/**): four-piece + an authorized APPROVED, ruling C by the landing seat.

查重词

objectui lock 1 enqueue class · enable_pr_auto_merge deny mirror · disable_pr_auto_merge settings.json objectui · permissions.deny mirror PR 18317 · declared equals enforced enqueue


Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 22, 2026

    @huangyiirene
    Collaborator

    Claim: PM loop round R2 (skills seat 1) — batch 5 on the maintainer's word of this date (「并发加到5」; 「你可以不用下班了,继续派发」 after the shift-end brief)
    Session: session_01Wnstp2kTth7sGXfr8fXypc
    Branch: claude/issue-9586-settings-deny-auto-merge-mirror
    Worktree: objectui-issue-9586
    Domain: domain:skills
    Seat: domain:skills#1
    File surface: objectui .claude/settings.json (permissions.deny gains mcp__github__enable_pr_auto_merge and mcp__github__disable_pr_auto_merge beside the existing mcp__github__update_pull_request entry — the mirror of objectstack PR #18317, ruling A on objectstack#18282), the objectui roster gate that pins the deny count if one exists (objectstack's is scripts/pm/check-settings-deny-roster.mjs — measure objectui's equivalent, constant and cases), and the one fact line in objectui's channel table if it carries one (objectstack's is rest-channel.md :53 「auto-merge MCP 锁 1 同拒」); ⛔ nothing else (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: TIER_DEFAULT — the sister repo derives nothing on this tree; hand-derived: objectui .claude/settings.json is on none of the three mandate globs ⇒ TIER_DEFAULT; governed under objectui's own register (.claude/**) — the dev runs objectui's check-governed-merges.mjs --pr and reports the tier; the lane's review of record is rendered in-seat at the served tier (fable).
    Clause-②: no
    Thread-read: none
    Ruling-ref: objectstack#18282 — ruled A by the director seat, batch #135 追认 by the maintainer (「declared = enforced」 extended to the enqueue class); the objectstack landing PR #18317 (ceb6b5fb46ab) is the model; this card is the cross-repo successor the ruling names
    Serial constraints cleared: objectui open PRs' file lists read at 2026-09-22T14:17Z by the dev before editing (objectui#10212 / #10213 hold AGENTS.md only); git ls-remote --heads origin on objectui scanned for settings and 9586: no branch.

    Dispatch basis: a ruled cross-repo mirror (the enqueue class joins the deny list; the seat's only enqueue route on both boards is already REST PUT …/ccr/auto_merge, so no capability is lost). 北极星「优先级」第 3 条 read fresh at 2026-09-22T14:17Z (objectstack non-epic p0 0 · p1 27; objectui p0 0 · p1 8): applied by this seat as label-based (seat post #7623 r15) — this card carries no tooling label and is not contract hygiene. Premise re-read on objectui origin/main by the dev before editing (the deny list read 14 mcp__github__* entries at the seat's stale read of 0cf2d6644; measure fresh); premise_still_valid: false is a first-class outcome.


    Generated by Claude Code

  2. self-assigned this
    on Sep 22, 2026
  3. huangyiirene commented on Sep 22, 2026

    @huangyiirene
    Collaborator

    os-dev-report

    {
      "issue": 9586,
      "status": "done",
      "branch": "claude/issue-9586-settings-deny-auto-merge-mirror",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10214",
      "session": "session_01Wnstp2kTth7sGXfr8fXypc",
      "premise_still_valid": true,
      "summary": "objectui .claude/settings.json permissions.deny goes 14 -> 17: the enqueue pair mcp__github__enable_pr_auto_merge / mcp__github__disable_pr_auto_merge (the card criterion, mirror of objectstack PR #18317) plus mcp__github__update_pull_request, which the card measured absent here too (objectstack PR #18276 never mirrored). H1 measured on the base: all THREE absent, 14 mcp__github__* entries, so the premise holds and the card ruling names all three. Order convention measured as grouped-by-class, NOT alphabetical: update_pull_request inserted next to create_pull_request and the enqueue pair appended, which makes objectui deny element-for-element identical to objectstack deny, order included. allow and hooks byte-identical to origin/main; an empty-frontmatter changeset declares no release per this repo convention. PR is draft and stays draft (governed surface, exit 3).",
      "deny_list_before_after": {
        "before": {
          "total": 14,
          "all_mcp_github": 14,
          "update_pull_request": "ABSENT",
          "enable_pr_auto_merge": "ABSENT",
          "disable_pr_auto_merge": "ABSENT",
          "positive_control": "mcp__github__issue_write present x1"
        },
        "after": {
          "total": 17,
          "update_pull_request": "PRESENT x1 (index 3)",
          "enable_pr_auto_merge": "PRESENT x1 (index 16)",
          "disable_pr_auto_merge": "PRESENT x1 (index 17)",
          "duplicates": 0,
          "removed": []
        },
        "equality": "branch deny === objectstack deny (order included): true",
        "invariance": "allow sha 46560076ce2be885 = 46560076ce2be885; hooks sha 10e6c3a3ae991607 = 10e6c3a3ae991607; top-level and permissions key sets unchanged; git diff --numstat 4 1 .claude/settings.json",
        "negative_controls": "mcp__github__actions_run_trigger 0, mcp__github__resolve_review_thread 0 (the two state-shaped tools NOT ruled closed stay out)"
      },
      "readback_criterion": {
        "call": "GET /repos/objectstack-ai/objectui/contents/.claude/settings.json?ref=claude/issue-9586-settings-deny-auto-merge-mirror",
        "http": 200,
        "blob_sha": "30c3367037de7ba000673e4da8caa2913a9c704c",
        "size": 2255,
        "decoded_deny": [
          "mcp__github__issue_write",
          "mcp__github__create_pull_request",
          "mcp__github__update_pull_request",
          "mcp__github__add_issue_comment",
          "mcp__github__add_comment_to_pending_review",
          "mcp__github__add_reply_to_pull_request_comment",
          "mcp__github__pull_request_review_write",
          "mcp__github__push_files",
          "mcp__github__create_or_update_file",
          "mcp__github__delete_file",
          "mcp__github__create_branch",
          "mcp__github__sub_issue_write",
          "mcp__github__merge_pull_request",
          "mcp__github__create_repository",
          "mcp__github__fork_repository",
          "mcp__github__enable_pr_auto_merge",
          "mcp__github__disable_pr_auto_merge"
        ],
        "verdict": "both enqueue entries present in the decoded permissions.deny -> the card one executable criterion is MET"
      },
      "roster_gate_reading": "ABSENT on objectui, gate NOT ported (per the dispatch: 新增门禁默认否). scripts/pm/check-settings-deny-roster.mjs does not exist; scripts/pm/ holds exactly board-snapshot.mjs and check-half-states.mjs. Repo-wide grep for settings.json across scripts/ and .github/ returns zero readers of this file: two hits only, a test fixture literal in check-shell-escape-residue.test.ts and a prose mention inside upstream-port-pin.json. So nothing on this board pins the deny count and no constant or case moved with the entries.",
      "channel_table_reading": "ABSENT on objectui, nothing added. No references directory exists anywhere under .claude/ (objectstack equivalent is .claude/skills/pm-dispatch/references/rest-channel.md). objectui .claude/ holds 14 files total: 4 hooks + 4 selftests + launch.json + settings.json + 4 markdown files across two skills (objectui-contributor, verify). The one fact line has no home on this tree.",
      "governed_merges_reading": "objectui carries NO scripts/pm/check-governed-merges.mjs (that is objectstack tooling; per objectui AGENTS.md it already covers this board as a report-only post-merge audit from objectstack side). The local governed reading is scripts/check-governed-queue-guard.mjs --test .claude/settings.json .changeset/... => exit 3, GOVERNED, 1 of 2 paths on .claude/** (the agent instruction tree). Model tier: TIER_DEFAULT as hand-derived by the PM; no objectui script derives a tier. PR #10214 is draft, never flipped ready, never enqueued, never armed with auto-merge, never approved by a seat. PR labels read after creation: [] (empty); needs:contract-review NOT present; nothing hung or removed by me.",
      "gates": [
        {
          "family": "json validity",
          "command": "node -e JSON.parse(...) ; python3 -c json.load(...)",
          "exit": 0,
          "verdict": "node JSON.parse OK / python json.load OK"
        },
        {
          "family": "control-bytes",
          "command": "node scripts/check-control-bytes.mjs (re-run AFTER commit so the new changeset is tracked)",
          "exit": 0,
          "verdict": "check-control-bytes: OK (scanned 8217 tracked text file(s); skipped 85 binary) -- 8216 before the commit, 8217 after, so the new file is inside the population"
        },
        {
          "family": "governed-surface-guard",
          "command": "node scripts/check-governed-queue-guard.mjs --self-test",
          "exit": 0,
          "verdict": "OK check-governed-queue-guard self-test: 185 cases pass"
        },
        {
          "family": "governed-surface-guard",
          "command": "node scripts/check-governed-queue-guard.mjs --test .claude/settings.json .changeset/issue-9586-enqueue-class-deny-mirror.md",
          "exit": 3,
          "verdict": "GOVERNED - 1 of 2 path(s) on .claude/** ; exit 3 is the healthy reading for this PR, not a failure"
        },
        {
          "family": "changeset-presence",
          "command": "node scripts/check-changeset-presence.mjs",
          "exit": 0,
          "verdict": "No source or published contract of a released package changed in this range, so no changeset is owed (1 changeset added anyway, per repo convention)"
        },
        {
          "family": "changeset-guard",
          "command": "node scripts/check-changeset-no-major.mjs",
          "exit": 0,
          "verdict": "No changeset declares a major bump"
        },
        {
          "family": "changeset-guard",
          "command": "node scripts/check-changeset-overwrite.mjs",
          "exit": 0,
          "verdict": "1 changeset(s) added, 0 modified, 0 deleted; no pre-existing changeset was modified or deleted"
        },
        {
          "family": "lint.yml pre-install",
          "command": "node scripts/check-lint-coverage.mjs",
          "exit": 0,
          "verdict": "lint coverage: 46/46 packages linted, 0 with outstanding errors"
        },
        {
          "family": "lint.yml pre-install",
          "command": "node scripts/check-entry-guard.mjs --self-test ; node scripts/check-entry-guard.mjs",
          "exit": 0,
          "verdict": "63 cases pass ; 108 scripts/ file(s) - no entry guard outside the baseline"
        },
        {
          "family": "lint.yml pre-install",
          "command": "node scripts/check-upstream-port-parity.mjs --self-test ; node scripts/check-upstream-port-parity.mjs",
          "exit": 0,
          "verdict": "66 cases pass ; 13 ported file(s) match objectstack modulo declared divergences (.claude/settings.json is NOT a pinned port, verified against the 13-entry ledger)"
        },
        {
          "family": "lint.yml pre-install",
          "command": "node scripts/check-bash32-floor.mjs --self-test ; node scripts/check-bash32-floor.mjs",
          "exit": 0,
          "verdict": "160 cases pass ; 13 tracked shell file(s) name no bash 4+ construct"
        },
        {
          "family": "skills-paths",
          "command": "node scripts/check-skills-paths.mjs",
          "exit": 0,
          "verdict": "88/89 stated path(s) resolve across 20 guide file(s); 1 baselined"
        },
        {
          "family": "line-citation-gate",
          "command": "node scripts/check-new-cross-file-line-citations.mjs",
          "exit": 0,
          "verdict": "0 new citation(s), enforcement report-only -> exit 0"
        },
        {
          "family": "hook-selftests",
          "command": "NOT OWED - hook-selftests.yml paths are .claude/hooks/** and the workflow file; this diff touches neither",
          "exit": null,
          "verdict": "not owed; separately verified that all 4 hook commands settings.json wires resolve on disk (4/4 OK), since this file is the one that names them"
        },
        {
          "family": "shell-escape-residue",
          "command": "NOT OWED - SCAN_ROOTS are AGENTS.md, CLAUDE.md, skills, .claude/skills, content/docs; .changeset/** is not among them",
          "exit": null,
          "verdict": "the new changeset is outside that gate population"
        },
        {
          "family": "pnpm lint / pnpm type-check",
          "command": "NOT RUN LOCALLY - declared narrowing, see deviations",
          "exit": null,
          "verdict": "NOT MEASURED locally; CI runs the farm on this PR (lint.yml relevance step excludes only **/*.md, content/**, docs/**, .changeset/** -- .claude/settings.json is not excluded, so should_run=true)"
        }
      ],
      "line_budget": "NOT APPLICABLE, measured rather than assumed: no gate on this tree prices .claude/** lines. check-skill-eval-tokens.mjs SCAN_ROOTS is [skills] only and its header states .claude/skills is deliberately excluded; a repo-wide grep for MAX_LINES / LINE_CEILING / line budget across scripts/ returns nothing. This diff adds no skills/** line. Net shape: +4 -1 on .claude/settings.json (three entries, one comma), plus a new 20-line changeset.",
      "files_changed": [
        ".claude/settings.json (+4/-1)",
        ".changeset/issue-9586-enqueue-class-deny-mirror.md (new, empty frontmatter, declares no release)"
      ],
      "tests": "No unit tests are owed: the diff is one JSON array and one changeset, and no gate on this board pins the deny roster (roster_gate_reading). Evidence is the 16-row gates table above, every exit captured by redirecting to a file BEFORE reading $?, never across a pipe. Commit 45f22049 is the tree every reading was taken on, with control-bytes re-run after the commit so the new file was inside its tracked population (8216 -> 8217). Mirror equality and invariance were measured, not asserted: branch deny === objectstack deny order included = true; allow/hooks hashes identical to origin/main; negative controls actions_run_trigger and resolve_review_thread stay at 0. Read-back criterion met over GET /contents (HTTP 200). No ablation: this PR adds no guard and weakens none, so there is nothing whose failure needs proving.",
      "mcp_calls": "0 - no MCP GitHub tool was called, read or write. All GitHub traffic went through the REST proxy with curl.",
      "api_writes": "2 REST writes: POST /repos/objectstack-ai/objectui/pulls (draft PR #10214, HTTP 201) and POST /repos/objectstack-ai/objectui/issues/9586/comments (this report). Plus 2 git pushes on the same branch, which are git-over-https and not REST: the mandated empty-branch routing probe before any edit (exit 0, no 403), then the commit. Zero label writes (objectui path labels belong to labeler.yml and the dispatch named none), zero PATCH of the PR body, zero writes outside the budget. No HTTP 429 and no pace-budget refusal was seen on any call.",
      "deviations": [
        "pnpm install was NOT run in the worktree, so pnpm lint / pnpm type-check are NOT MEASURED locally. This container has no node_modules for objectui (neither the shared checkout nor this worktree), the diff is one .json and one .md, and every gate this diff owes is a pre-install gate that lint.yml deliberately places before install (node builtins only). The narrowing is declared with its three readings in the PR body: (1) population read from eslint.config.js - every files glob is TS/TSX scoped, no JSON or markdown block; (2) the instrument count is SUBSTITUTED by an extension census - 0 of the 2 changed files carry a .ts/.tsx/.js/.mjs/.cjs extension, so the eslint population count is necessarily 0; (3) invariance - no block sets parserOptions.project or projectService, so type-aware linting is off and this diff cannot move the verdict on any untouched file. CI runs the full farm on this PR.",
        "Two git push invocations rather than one: the empty-branch routing probe required before any edit, then the commit. Both exit 0.",
        "The changeset act is a committed file, not an API write - matching how objectui PR #9448 (the prior mirror of this same settings file) declared a governed-only change: empty frontmatter, prose body, no skip-changeset label (that label is read by nothing on this board).",
        "The dispatch prompt file verified by md5 (f379114335205bfc9dae9625856d7ee7, matched) but its stated size did not match: the dispatch said 7482 chars, the file measures 8680 bytes and 8680 chars. md5 is the binding check per the dispatch and it matched, so I proceeded; reporting the mismatch rather than silently absorbing it.",
        "PR body was read back byte-for-byte after creation: 13328 sent, 13327 stored, and the delta is exactly the trailing newline (stored === sent minus trailing newline: true). Footer stored in session-URL form, session id also written as prose so it survives any later rewrite, 0 angle-bracket-shaped fragments, no HTML comments anywhere."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b - objectui AGENTS.md carries TWO recipe sites naming tools this PR denies, so after it lands a seat following the written instruction hits a runtime refusal. Site 1, inside the 自行合并 recipe code block: the comment line spelling the MCP equivalent as pull request update(draft: false) + enable_pr_auto_merge. Site 2, the governed-surface remedy bullet: 只调 disable_pr_auto_merge 会摘掉 auto-merge 但不取消队列成员资格,两个都要做. Contract text is this PR own permissions.deny (declared = enforced). Seam: spec:.claude/settings.json permissions.deny -> runtime:PreToolUse MCP refusal | consumer: AGENTS.md 自行合并配方段 + 受管面补救段. objectstack fixed its equivalent prose in the same landing (rest-channel.md line changed from auto-merge 备用 MCP 未拒 to auto-merge MCP 锁 1 同拒); objectui equivalent prose happens to live in AGENTS.md, which is NOT in this card file surface and is currently held by two in-flight PRs. Harm direction is the safe one: a stale recipe is refused loudly, not executed wrongly. dedupe words: AGENTS.md MCP 等价物 enable_pr_auto_merge · disable_pr_auto_merge 补救 两个都要做 · settings.json deny AGENTS.md 配方 漂移 · objectui 入队类 配方失效",
        "carrier: none that fits - PR #10213 does hold AGENTS.md and its hunk (@@ -323 +323 @@) sits in the same 自行合并 bullet as site 1, but it is scoped to a different clause (非受管 PR 由席位落地) and does not reach line 331 or line 531; PR #10212 hunk is at 468, a different section. Neither is a carrier for this text, so the seat should file it rather than fold it in. Not filed by me: dev does not POST /issues.",
        "class: none (acceptance note only, noted not filed) - .changeset/issue-9418-write-identity-locks-port.md, the never-consumed empty-frontmatter declaration from the previous mirror, still states that the four state-shaped tools including update_pull_request and enable_pr_auto_merge stay allowed. Two of them no longer are. It is a historical record with zero readers and no enforcement, so it is drift rather than a defect; recorded in the PR Acceptance notes so the next reader is not misled. carrier: none.",
        "class: none (acceptance note only, noted not filed) - scripts/check-governed-queue-guard.mjs refusal text still reads a human merge IS the review record for a governed surface, while AGENTS.md has moved to ruling C (authorized APPROVED review, then the claiming seat lands). Visible verbatim in the --test output quoted in the PR body. Already reported once in objectui PR #9448 Acceptance notes and still present; not re-filed."
      ]
    }

    Generated by Claude Code

  4. huangyiirene commented on Sep 22, 2026

    @huangyiirene
    Collaborator

    ACCEPT — domain:skills seat 1 (session_01Wnstp2kTth7sGXfr8fXypc, seat post objectstack#7623), 2026-09-22T15:19Z — PR #10214 at head 45f22049e02b3485e34c9475f1a3b29258700623 (round 0).

    Checklist, read against GitHub and the fetched head on a review worktree, not the report: draft, base main, body first line Fixes #9586, one commit, single parent, origin/main 0cf2d6644 an ancestor; two files: .claude/settings.json +4 / −1 (permissions.deny 14 → 17 — mcp__github__update_pull_request beside create_pull_request, mcp__github__enable_pr_auto_merge and mcp__github__disable_pr_auto_merge appended; nothing removed, no duplicate; the array after the change equals objectstack's deny on 106d4c8dd element for element, order included; allow, hooks and both key sets unchanged; the file parses) and .changeset/issue-9586-enqueue-class-deny-mirror.md (empty frontmatter — no release, the PR #9448 convention). The card's one executable criterion is met: the decoded permissions.deny read back over GET /contents on the branch carries both enqueue entries. Premise measured on the base: all three entries absent (the card's filing read holds; the seat's claim was wrong to assume update_pull_request present — the third entry is inside the card). The two optional halves both absent on this tree and both left alone: no deny-roster gate (⛔ not ported — 新增门禁默认否), no channel table (no references/ under objectui's .claude/). Gates re-run by the seat at the head: JSON parse, check-governed-queue-guard self-test (185) and --test (exit 3 GOVERNED, the healthy reading), check-control-bytes, the three changeset checks, check-upstream-port-parity, check-entry-guard, check-skills-paths, check-bash32-floor, check-lint-coverage, check-new-cross-file-line-citations — all 0; the four wired hook commands resolve. CI at this head: 42 latest-per-name check runs — 39 success, 3 skipped, 0 in progress, 0 other. Record: 5778587633 (PASS, rendered in-seat at the served tier). Report 5778518389 parses; mcp_calls 0; two REST writes plus two pushes; no label write, no body PATCH by the dev.

    Deviations recorded, none blocking: pnpm lint / pnpm type-check not run locally (no objectui node_modules on the container; the diff carries no TS / JS file; CI runs both); the dispatch file's stated size was a code-point count against the dev's byte count — the md5 matched; two pushes (the empty-branch probe, then the commit).

    Out of scope, reported by the dev (class b, ⛔ not fixed here): objectui AGENTS.md :331 and :531 name enable_pr_auto_merge / disable_pr_auto_merge as the MCP equivalents in the 自行合并 recipe and the remedy bullet — denied tools once this lands (refused loudly, not executed wrongly); #10212 (:468) and #10213 (:323) are on other regions, so no carrier. The filing door bars this seat's User-type identity; the item goes to the maintainer on the seat post. Two acceptance notes recorded, not filed: the stale 「stay allowed」 sentence in .changeset/issue-9418-write-identity-locks-port.md; the queue guard's 「a human merge IS the review record」 refusal text (reported on PR #9448 already).

    Landing: GOVERNED under objectui's register (.claude/**) — draft stays draft; the 四件套 in this act (this ACCEPT, needs-user-decision on the PR, 席位意见 in the body, review requested from os-zhuang / hotlong); on an authorized APPROVED review the seat lands it through the CCR routes (ruling C). At MERGED: the card closes by Fixes; the seat takes pm:dispatched and the assignee off.


    Generated by Claude Code

  5. removed their assignment
    on Sep 23, 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

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions