Skip to content

ci(fleet-write): grant permission-contents: write to the mint step so the relay can arm auto-merge - #19767

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-19762-fleet-write-contents-permission
Sep 23, 2026
Merged

hotlong merged 1 commit into
mainfrom
claude/issue-19762-fleet-write-contents-permission

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #19762

What changed

Touches .github/workflows/**, so this is a draft the maintainer merges by hand.

  1. The mint step grants permission-contents: write. In .github/workflows/fleet-write.yml, actions/create-github-app-token@v2 gains permission-contents: write beside the three existing permission-* inputs, with a comment naming the one consumer — GitHub's enablePullRequestAutoMerge / disablePullRequestAutoMerge mutations require contents: write on the token (measured on the relay's first live automerge_enable: "Resource not accessible by integration") — and stating that the op table has no contents op, so the grant is spent only by those two mutations. The header's permission list says the same.
  2. The op table spends it truthfully. PERMISSIONS in scripts/pm/fleet-write/ops.mjs gains contents: 'write'; the automerge_enable and automerge_disable rows carry permission: 'contents', so the validator's permission read stays a true statement of what each row spends. The header paragraph on permissions is updated.
  3. Self-tests (validate.mjs --self-test, the refused-by-construction battery, 9 → 14 pinned cases): the map is exactly issues / pull-requests / contents write and metadata read; the two auto-merge rows spend contents; no other row does; every contents spender is an auto-merge GraphQL mutation, never a REST path; and the workflow's permission-* inputs, read from the file on disk, mirror PERMISSIONS exactly — a grant added on one side without the other reds.
  4. Nothing else. No new op, no change to the sender gate, caps, ceilings, the executor or the four write tools.

Verification record

Batteries, each run alone with its exit captured before any pipe (worktree at 182209b, base origin/main dc1b986):

command exit verdict line
pnpm check:pm-fleet-write-validate 0 74 cases pass across 7 batteries (was 69)
pnpm check:pm-fleet-write-execute 0 48 cases pass across 9 batteries
pnpm check:pm-fleet-write-dispatch 0 64 cases pass across 9 batteries
pnpm check:pm-with-fleet 0 31 cases pass
pnpm check:pm-post-stamped 0 596 cases pass across 20 batteries
pnpm check:pm-label-write 0 88 cases pass across 10 batteries
pnpm check:pm-issue-create 0 42 cases pass across 6 batteries
pnpm check:pm-close-cards 0 111 cases pass across 12 batteries
pnpm check:pm-write-pace 0 109 cases pass across 12 batteries
pnpm check:workflow-status-functions 0 OK (39 workflow files)
pnpm check:workflow-step-name-quoting 0 OK (39 workflow files + 2 composite actions)
pnpm check:required-contexts 0 OK

npx eslint --no-inline-config on the two changed .mjs files: exit 0.

Gates — node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands on the edited tree derived 54 families (3 paths vs merge base dc1b986; 52 changed lines, under the human-merge threshold). Each was run alone with its exit code captured before any pipe; all 54 exited 0. --ran with the codes recorded: 54 derived famil(ies) accounted for — 54 run, 0 NOT-MEASURED (a DERIVED zero — all 54 recorded an exit code and none of them is 3). Repo-wide pnpm lint is CI's run; the changed files were linted directly (above).

The live grant itself cannot be proven before merge (a workflow change runs only once it is on main); the card's post-merge verification is the director seat's: a two-action relay request (pr_draft + pr_ready, or automerge_enable on the next queued PR) reading back enabledBy = objectstack-fleet[bot].

Changeset

None: .github/workflows/** and scripts/pm/** publish nothing from any released package, so skip-changeset applies. This dispatch forbids label writes, so the label is the seat's to apply.

Acceptance notes


Generated by Claude Code

…can arm auto-merge

GitHub's enablePullRequestAutoMerge / disablePullRequestAutoMerge mutations
require contents: write on the App token; the relay's first live
automerge_enable answered "Resource not accessible by integration" with the
token narrowed to issues, pull-requests and metadata alone.

The mint step (actions/create-github-app-token@v2) gains
permission-contents: write beside the three existing permission-* inputs,
with a comment naming the one consumer and that the op table has no contents
op. PERMISSIONS in ops.mjs gains contents: 'write', and the automerge_enable /
automerge_disable rows spend it, so the validator's permission read stays
truthful. The validator's self-test pins the map entry by entry, the two rows,
that no other row spends contents, that every contents spender is an
auto-merge GraphQL mutation, and that the workflow's permission-* inputs
mirror PERMISSIONS exactly. Nothing else changes: no new op, the sender gate,
caps and ceilings untouched.

Claude-Session: https://claude.ai/code/session_01GnJon4xkRvphn4w28xx3An
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 23, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

席位:总监席(summon #27,session_0129ZpnaBcYZZ51rCvQiXg6C),2026-09-23T01:49Z。这是 #19762 的交付,.github/workflows/**(席位令牌无 workflows 范围),等您人合。

改了什么:fleet-write.yml 铸 token 那一步加 permission-contents: write(注释写明唯一消费者是 GitHub 的 enable/disablePullRequestAutoMerge,op 表没有任何 contents 类操作);ops.mjs 权限表同步加 contents: 'write',两条 auto-merge 行改为消耗它;validate.mjs 自测把权限表、两行、以及 workflow 里的 permission-* 输入与表逐项钉死相等。三个文件 +42/−10。

为什么改:#19755 落地后第一笔真实使用实测:中继翻 ready 成功,开 auto-merge 被拒「Resource not accessible by integration」——正是 #19701 里「先不预授、被拒再加」的那一条。没有它,车队入队的最后一步仍要用席位自己的用户身份。

风险与代价:token 多一项 contents: write,但 op 表没有写文件/分支/ref 的操作,自测钉死了表与 workflow 一致,能花掉它的只有两条 auto-merge mutation;token 仍是逐次铸造、窄到目标仓、步骤结束即撤销。回滚 = 撤这一行与表项。

席位意见:通过。CI 绿(Check Changeset 那次红是缺 skip-changeset 标签,席位已补);check-governed-merges --pr 19767 0/3 命中登记册。

你要做的:合并本 PR(Squash)。合并后我用下一张入队的 PR 回读 autoMergeRequest.enabledBy == objectstack-fleet[bot]。


Generated by Claude Code

@hotlong
hotlong marked this pull request as ready for review September 23, 2026 02:34
@hotlong
hotlong added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 90d65e4 Sep 23, 2026
40 of 41 checks passed
@hotlong
hotlong deleted the claude/issue-19762-fleet-write-contents-permission branch September 23, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd needs-user-decision size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants