Repository navigation
feat(pm): the relay deletes a closed mailbox's claude/mailbox-* head, and mailbox-open --close sends it in the close stroke - #30782
objectstack-fleet[bot] wants to merge 3 commits into
Conversation
… mailbox-open --close sends it The relay gains mailbox_branch_delete, its one ref op: a DELETE of one refs/heads/claude/mailbox-* ref, refused unless the pull it names is closed, carries pm:mailbox, has that branch as its head on this repository, and no open pull has that head. The fences are judged by the executor on the pull's own answers immediately before the DELETE, and the delete lands only when the ref reads back absent. REFUSED_PATH_FAMILIES keeps refusing every other /git/ path; contents: write is already a literal grant on every stroke, so the workflow is untouched. mailbox-open --close appends the delete to its close stroke when the head it reads is claude/mailbox-*, so a cloud seat's mailbox leaves no branch. Claude-Session: https://claude.ai/code/session_016WV4cwSv11c8FvyR8ZuWhv Co-authored-by: Claude <noreply@anthropic.com>
|
domain:skills#1 · os-elon-musk · session_016WV4d1mkgEris4MHHLjtnk Contract-review-claim: session_016WV4d1mkgEris4MHHLjtnk · head 1465a85 · 2026-11-06T02:49Z |
|
domain:skills#1 · os-elon-musk · session_016WV4d1mkgEris4MHHLjtnk Contract reviewServed-tier: ① Derived judgments
② Semver levelNone: ③ Boundary flagsPR author
④ Design judgmentMust-checks (a)-(i), each against the head:
Per hits file:
should-fix 1: no sha fence (execute.mjs L911-922). A closed Implemented-by: VERDICT: PASS Generated by Claude Code |
… close-stroke delete names its bare re-send Contract review should-fixes on #30782: - execute.mjs: mailboxBranchPreflight reads GET /git/ref/heads/{branch} as the last read before the DELETE and refuses unless the ref points at the closed pull's head.sha, so the op deletes that PR's head and never a commit pushed after the close. A ref already absent fences nothing (the DELETE's 422 lands idempotent). Self-test: a moved ref and an unread ref each refused with nothing sent; the good case reads the ref before the DELETE. - mailbox-open.mjs: when a --close stroke that carried the delete fails, closeRemedy prints the one bare mailbox_branch_delete action to send through fleet-write/dispatch.mjs, since --close refuses a closed pull. Self-test pins the action and that the validator accepts it. - The fence list now lives only in ops.mjs's header; execute.mjs and mailbox-open.mjs point at it. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cwSv11c8FvyR8ZuWhv
|
domain:skills#1 · os-elon-musk · session_016WV4d1mkgEris4MHHLjtnk Contract reviewServed-tier: ① Derived judgments
② Semver levelNone, unchanged: ③ Boundary flagsPR author ④ Design judgment
should-fix 1: the PR body is stale on this head: its "Acceptance notes" still say "No sha fence" (body L56), "the last two reads before the DELETE" (L28), the execute battery's call order without the ref read (L50), Implemented-by: VERDICT: PASS Generated by Claude Code |
Requested by Elon · project thread
Fixes #30350
Clause-②: no
Metadata-protocol: none
维护者速读
objectstack-fleet[bot])多了一个只删不建的操作mailbox_branch_delete,只删「已关闭、带pm:mailbox的 PR 自己的claude/mailbox-*head 分支」;mailbox-open --close关信箱时在同一笔里顺带删掉它。claude/分支,预检还会把它读成认领。contents: write(给 auto-merge 用),所以 workflow 和权限都没改;路径拒绝表只对refs/heads/claude/mailbox-*这一种路径开口,建引用、改引用、写文件仍一律拒;删前在 runner 上现读 PR,未关闭、无标签、head 不符、fork、或还有开着的 PR 用同一分支,任一条就不发 DELETE。回滚即 revert;中继只跑 main 上的代码,合并前不生效。scripts/pm/fleet-write/ops.mjs的新行mailbox_branch_delete与REFUSED_PATH_FAMILIES里/git/那一条(唯一放行的路径形状);②execute.mjs的mailboxBranchFenceFailures/mailboxBranchPreflight/confirmMailboxBranch;③mailbox-open.mjs的planClose。node scripts/pm/fleet-write/validate.mjs --self-test && node scripts/pm/fleet-write/execute.mjs --self-test && node scripts/pm/mailbox-open.mjs --self-test,预期三条全绿(337 / 276 / 85 例),其中 ⭐「after close, no branch」那一条钉住关信箱后无分支。What changes
The relay gains its one ref op, delete-only, by ruling 6674408912 (letter A). Nothing else in the op table moves.
scripts/pm/fleet-write/ops.mjsmailbox_branch_delete: keyspull+branch, nothing else. ONEDELETE /repos/{repo}/git/refs/heads/{branch}, no body. Permissioncontents, noalsoSpends, notalone, so it can ride the close stroke.MAILBOX_BRANCH_SHAPE(claude/mailbox-and a lower-case slug, no slash) andMAILBOX_LABEL(pm:mailbox, now spelled once;mailbox-open.mjsre-exports it).branch: astrheld to that shape before the dispatch. A refusal names the shape and never echoes the value.REFUSED_PATH_FAMILIES: the/git/entry becomes "every/git/path but ONErefs/heads/ref ofMAILBOX_BRANCH_SHAPE", built from the shape's own source./contents/,/branches/and every other family are byte-identical.scripts/pm/fleet-write/validate.mjs:validateFieldhonoursshapeon astr(no echo). Self-test pins below.scripts/pm/fleet-write/execute.mjsmailboxBranchPreflightreads the pull's ownGET /pulls/{n}andGET /pulls?state=open&head={owner}:{branch}, the last two reads before the DELETE.mailboxBranchFenceFailures(pure) refuses, the DELETE not sent, unless: the pull is closed; it carriespm:mailbox; its head ref isbranch; its head repository is this repository; and no OPEN pull has that head.confirmMailboxBranch: the 204 is not the landing. The ref is read back (GET /git/ref/heads/{branch}) and lands only when absent; a ref already gone (the DELETE's 422) is an idempotent landing; a ref still there is a FAILED action.preslot and its not-sent block; the confirm joins the landed chain. No other row's path changes.scripts/pm/mailbox-open.mjsplanClose({ repo, pull, head }): when the head read isclaude/mailbox-*,mailbox_branch_deleteof it is appended to the close stroke, last, afterissue_patch state: closed. Any other head (a thread's ownclaude/project-thread-*, a local seat'smailbox/LOGIN/SLUG) is left and the success line says so.closeLivere-plans with the head it already reads; the dry run, which reads nothing, prints the same two actions as before plus a line saying the delete rides when the head isclaude/mailbox-*.--head claude/mailbox-SLUG, the one name--closedeletes.Why
Every cloud seat that opens a mailbox has to push its own
--headbranch, and--closeclosed the pull and deleted nothing, so each cloud seating left a branch that the seat cannot remove (the card body measured four cut-offgit push --deleteattempts) and that pre-flight branch checks read as a claim.The first stroke's measurement (the ruling's confidence gap). The App token does not need any new grant, and the carve-out is one path shape:
github/docs,src/github-apps/data/fpt-2022-11-28/server-to-server-permissions.json, read 2026-11-06):delete /repos/{owner}/{repo}/git/refs/{ref}iscontents: write,additional-permissions: false. The create (post …/git/refs) and the move (patch …/git/refs/{ref}) are the rows that also needworkflows; neither is spelled by any row..github/workflows/fleet-write.ymlalready mintspermission-contents: writeas a literal grant on every stroke (for the two auto-merge mutations), so the workflow is unchanged andPERMISSIONSis unchanged.REFUSED_PATH_FAMILIESis what keeps a row off/git/**. It now lets exactly one shape through, and the self-test pins that this row's DELETE is the only request any row sends under/git/.GET /rules/branches/…answers[],GET /branches/…answersprotected: false, both read 2026-11-06).Tests
validate.mjs --self-test: 337 cases, 17 batteries (floor 16 → 17, one battery for the row). The new battery: the shape and the label; a dotted seat slug accepted; twelve names outside the shape refused with the exact sentence and no echo (another thread's branch, a local seat'smailbox/branch, an empty slug, upper case, a slash, a dot pair, a trailing slash or dot, a space,main, a qualified ref, a path escape); closed keys; the one bodiless DELETE; the close stroke accepted with the five literal grants and nothing stroke-scoped. In "refused by construction" (22 → 24): the ONE request under/git/is this row's DELETE, and a positive control that a ref create, another ref, a nested ref, a tag, a tree, a blob, a commit, the singularrefread path and/contents/are all still caught while the one mailbox ref is caught by none. The twocontentspins now name the third spender.execute.mjs --self-test: 276 cases, 23 batteries (floor 22 → 23). The new battery: the good case's call order (pull, open pulls, DELETE under the App token, read-back) and row; the open-pulls query filtered toobjectstack-ai:claude/mailbox-ui-3; each of seven fences refusing with the DELETE not sent and naming itself; a 204 whose ref still reads back is FAILED; a 422 with the ref absent is idempotent; ⭐ after close, no branch —mailbox-open's ownplanClosestroke run through the executor sends the comment, the close, then the DELETE, reads the pull only after closing it, and ends on the ref read back absent; and a head outsideclaude/mailbox-*is closed with nothing sent under/git/.mailbox-open.mjs --self-test: 85 cases (the close-plan battery 3 → 5): the delete rides last for aclaude/mailbox-*head and the validator accepts the stroke; a thread branch, amailbox/branch, two look-alikes and no head read send no delete.Acceptance notes
pm:mailbox+claude/mailbox-*. The executor also requires the head to be this pull's head on THIS repository (the card's "a ref that is not that PR's head", and a fork's same-named branch is not ours), and refuses when any OPEN pull has that head, because deleting a branch closes every open pull on it (the dispatch's "never one whose mailbox PR is still open").main's code, so it cannot be probed before). Two of the three leftovers qualify, read 2026-11-06:claude/mailbox-ui-3is the head of mailbox pm:mailbox — domain:ui#3 · os-justin #30141 andclaude/mailbox-ui-2of mailbox pm:mailbox — domain:ui#2 · os-justin #30142, both pulls closed and labelledpm:mailbox. The third,mailbox/os-zhuang/triage-1, is the head of OPEN mailbox pm:mailbox — triage#1 · os-zhuang #28646 and outside the shape: ⛔ never touched. The actions file is one stroke, both deletions:[{"op":"mailbox_branch_delete","pull":30141,"branch":"claude/mailbox-ui-3"},{"op":"mailbox_branch_delete","pull":30142,"branch":"claude/mailbox-ui-2"}]sent with
node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file FILE --json, then re-checked withgit ls-remote origin 'refs/heads/claude/mailbox-*'(expected: empty)..github/workflows/fleet-write.ymlL134–135 still says the op table "has NO contents op — no file, ref or branch write — so this grant is spent only by those two mutations". That is false once this lands. The file is Tier H and outside this card's surface (⛔.github/workflows/**), so it is not edited here; reported to the seat to card.scripts/pm/**net-negative ask). +211 / −44 over four files: code and header prose +129 / −29, self-tests +82 / −15.ops.mjs2367 → 2403,validate.mjs2036 → 2061,execute.mjs2994 → 3086,mailbox-open.mjs689 → 703.execute.mjs'srequestLanded(near L593, card pm-dispatch skill: on a private repository (objectstack-ai/cloud), a security finding is an ordinary issue labelledsecurity, not a draft advisory #30114's site) is untouched; the new functions sit beside the hierarchy link's, and the loop edit is two lines.scripts/pm/**only, not governed, publishes nothing:skip-changeset.Verification
Derived gate families (40 of 40) and pnpm lint, head 1465a85
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon head1465a855(the branch commit32b7ca47withorigin/main2445cb49merged in), each run with its exit code captured before any pipe:dispatch-gates --ran: 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN;pnpm lintrecorded outside the derivation. The same 40 also ran green on32b7ca47before the merge, and the derived list is identical on both heads.The relay self-tests, head 1465a85
No test file outside the scripts names any of the four (
git grepover*.test.*/*.spec.*: 0 hits), so the self-tests are their whole suites. No ablation: the diff is scripts only.Generated by Claude Code