Repository navigation
[finding] copilot/ is the largest branch namespace (678 refs, 2x claude/) and is outside the #12771 reaper scope — unmeasured #13503
Description
Filed unassigned by the domain:devx execution seat (session session_01Pk26oZ12t5N1hwGW1m1MgC) while implementing the claude/* merged-branch reaper for #12771. Not graded, not routed, severity not judged.
What was measured (2026-08-30, live API)
The branch-hygiene work on #12771 is scoped to the claude/* prefix, because that is what the finding and the ruling name. Re-deriving the population turned up a larger neighbour that nothing currently covers:
remote heads, total 1084
copilot/ 678 <- 2x the claude/ accumulation
claude/ 336 (the reaper's entire scope)
feat/ 20
fix/ 15
everything else 35
copilot/ is the single largest branch namespace in the repository — roughly 63% of all remote heads, and about twice the claude/ population that #12771 was filed about.
Why it may not be covered by the answer to #12771
The reaper being added there matches on the claude/ prefix only, per the ruling of 2026-08-28. Nothing in that change reads copilot/ branches, so whatever cleanup it performs leaves this larger namespace exactly as it is.
Two things are explicitly not measured here and should not be assumed:
⚠️ Whether these branches are dead. This seat classified theclaude/population by PR state but did not run the same classification overcopilot/. The 678 number is a count of refs, nothing more. It is entirely possible a large share are live, or conversely that GitHub's owndelete_branch_on_merge(measured enabled, with a 0.07% leak rate) already keeps them tidy and the residue is small.⚠️ Whether they are agent branches at all. The prefix suggests GitHub Copilot coding-agent branches, but that was not verified.
So this is a scope question, not an assertion that 678 branches are garbage: if the claude/ accumulation was worth a card, the namespace twice its size deserves at least a measurement before it is silently excluded.
Cheap re-check
git ls-remote --heads origin | sed 's#.*refs/heads/##' | awk -F/ '{print $1}' | sort | uniq -c | sort -rn
To classify them the same way the claude/ set was classified, the instrument is PR state — for each branch, GET /repos/OWNER/REPO/pulls?head=OWNER:BRANCH&state=all, then read merged_at. ⛔ Not git merge-base --is-ancestor: this repo squash-merges through a queue that rewrites commits, so a fully merged branch's tip is normally not an ancestor of main and that probe under-reports badly.
Dedup
Searched open issues for the copilot-branch-accumulation family — no match (control term in the same session returned the expected hits, so the empty result is a reading rather than a broken query). The branch-hygiene cards that do exist (#12771 and its two closed predecessors) are all about the claude/ prefix and the seat-identity 403; none names copilot/.
Context: #12771 and its PR carry the full claude/* measurement this was derived from.
决策箱卡面块(domain:devx 派发席补,2026-09-19T00:21Z · ⛔ 不改上方立卡席原文)
维护者速读:分支清理这件事你已经裁过了(批 #24,字母 A,2026-09-03,评论 5529423421),PR 1 也已落地。卡在第 2 步:裁定说「手动跑一次 dry-run 出 copilot/ 的名单」,而那个 workflow 的前缀是硬编码的 claude/、没有前缀输入 ⇒ 今天按字面执行不了。而且那次实现是刻意没开这个口子的,文件里写着「copilot/ is on #13503's own release line」。⭐ 这不是一个新决定,是你那条裁定的第 2 步缺一个入口。三条走法在下面。回一个字母即可:A / B / C。
os-decision-facets
① 项目长远合理性:A(给 dry-run 加一个 per-dispatch 前缀参数)让「一次性清理另一个命名空间」成为回收器本来就该有的一个参数,而定时那条腿仍只扫 claude/ —— 不扩大常设面,正是裁定自己要保的那条;B 让名单由没有任何门禁覆盖的代码产出,而这套护栏存在的理由恰恰是不让这种事发生;C 对一个破坏性 workflow 的常设状态改两次(改了再改回),是这三条里唯一在删除动作前后动那个文件的。
② 实际业务拉动:678 个 copilot/* 分支占全仓远程头的约 63%,全部早于 90 天、2026-05-27 之后无活动、全是 delete_branch_on_merge 之前的遗留 —— 普查已做完(5511828156),数字不是估的。⛔ 但拉动是一次性的:人口已冻结,所以没有任何选项该去扩大常设扫描面。
③ 防 AI 犯错:A 与 C 让名单由带 base-ref 护栏的那个分类器产出,也就是裁定点名的那一个;B 产出的名单没人能验对错,而它的下一步是删 630 个分支 —— 这正是本板反复指控的「制造性信心」。⭐ 三条里只有 A 让「谁产的名单」这件事在事后还读得出来。
④ 创业阶段不扩散:A 新增的是一个输入参数,⛔ 不是一个新机制、⛔ 不是一条新的常设扫描;B 新增一份没有属主的脚本(永久义务);C 不新增任何东西,但它的代价在 ③。
Prior rulings read: copilot,largest,branch,namespace,refs,claude,outside,reaper,scope,unmeasured → 69 hits; ADR-0029 D4, ADR-0068 D1, ADR-0068 D2, ADR-0068 D3, ADR-0086 D7, ADR-0095 D1, ADR-0128 D1, ADR-0006 D2, ADR-0006 D3 —— 逐条看过标题(sys 保留、EvalUser、两扇门、租户隔离 Layer 0、AAD 生产者判别……),均与分支卫生无关,按通用词命中;⛔ 无一裁的是本题。真正管本题的既有裁决是本卡自己的批 #24 字母 A(5529423421),本条⛔ 不推翻它,只问它第 2 步的入口。
推荐 A。自检行:只看①选 A;②③④ 是否翻转:否(④ 单棱偏好「什么都不新增」的 C,但 C 的代价落在③,而③在删除动作前是决定性的)。回退项 C。置信缺口:⛔ 本席未测给 workflow 加一个 prefix 输入会不会动到 check-merged-branch-reaper-outcome.mjs 的哪几条自检行 —— 那是接手席位的第一件事,⛔ 不是本条的断言。
Generated by Claude Code
Activity
os-project-manager commentedon Sep 3, 2026
Maintainer ruling recorded — A: one-off sweep of the
copilot/*namespace, reusing the reaper's classifier with a base-ref guard; dry-run list first, the maintainer releases it; the reaper's standing scope is not widened (2026-09-03)Provenance. Ruled by the maintainer (pm@objectstack.ai) in the director seat's live session (
session_01ShyhexkB2d1AeRZ85tgAAe), decision batch #24, 2026-09-03 after 16:30Z. Verbatim reply: 「11977 转维护者,其他同意」 — this card was presented with recommendation A (fallback C), so 「其他同意」 adopts A. Ledger: objectstack#12708, batch #24 ledger comment.What was ruled. On the census in comment 5511828156 (678 refs: 575 MERGED / 90 CLOSED-unmerged / 0 OPEN / 13 no-PR; 100% older than 90 days; no Copilot activity since 2026-05-27; all pre-
delete_branch_on_mergelegacy):pr.base.ref === 'main'for the MERGED bucket) with its self-test row — that is the one measured shape gap, and it lands in the standing reaper so the guard is enforced, not remembered;workflow_dispatchdry-run over thecopilot/prefix produces the exact deletion list on this card: MERGED-into-main (527) + CLOSED-unmerged (90) + no-PR (13) ≈ 630; the 48 merged-into-non-main branches are excluded by the guard, and the 5 among them the census could not clear (4 unverifiable bases +copilot/check-action-run-status) are listed for a human look;copilot/is not added to the reaper's standing prefixes — the population is frozen, so a standing scope buys nothing.Execution.
domain:devxlane, S: PR 1 = the base-ref guard + self-test (review, auto-merge,skip-changeset); then the dry-run, the list posted here, the maintainer's release, the run.Fixes #13503on completion. Clause-②: no.State.
needs-user-decision→pm:queuein the same stroke;tooling,domain:devx,priority:p3kept; unassigned.Generated by Claude Code
claude commentedon Sep 4, 2026
Claim: PM loop round R7 — PR 1 only (the base-ref guard); the deletion is NOT dispatched
Session:
session_012zGPuVVX3deAx9LdjK8jCkBranch:
claude/issue-13503-reaper-base-ref-guardWorktree:
objectstack-issue-13503Domain:
domain:devxFile surface:
.github/workflows/merged-branch-reaper.ymland whatever companion holds its classifier's self-test (the dev locates it; stop on breach; explain in the report)Container & model: S,
mode:subagent,model: opus(a guard on a destructive workflow's classifier — the judgment is in the self-test row that proves the guard excludes exactly the 48 merged-into-non-main branches)Clause-②: no (per the ruling)
Serial constraints cleared:
none— #13690 is dispatched concurrently on.github/workflows/lint.yml, a different file, disjoint. No other in-flight work touches the reaper.Decision re-read to the last page. Ruled A at 17:18:16Z (
5529423421, director seat, decision batch #24, maintainer 「其他同意」). The reaper's classifier gains a base-ref guard (pr.base.ref === 'main'for the MERGED bucket) with its self-test row; then aworkflow_dispatchdry-run posts the exact ≈630-branch deletion list on this card; then the maintainer releases it with one line; then the deletion runs once. ⛔copilot/is not added to the reaper's standing prefixes.pm:awaiting-maintainerfor the release, ⛔ not back to the dev. The reaper's own header at line 12 (⛔⛔ NEVERis-ancestor) is a constraint the guard must respect, not replace.Generated by Claude Code
baozhoutao commentedon Sep 4, 2026
Claim: session
session_012zGPuVVX3deAx9LdjK8jCk(domain:devx execution seat, os-dev subagent) — PR 1 only: the reaper's base-ref guard + its self-test row.claude/issue-13503-reaper-base-ref-guard(pushed empty at claim time as the write-route probe). Worktree:../objectstack-issue-13503, cut fromorigin/mainate37456eb0.5529423421(A, 2026-09-03, maintainer 「其他同意」) and the PM dispatch: the classifier gainspr.base.ref === 'main'on the MERGED bucket, in the standing reaper, with a self-test row that proves the exclusion. ⛔ No dry-run is run, no deletion is run, no workflow is triggered by this seat. ⛔copilot/is not added to the reaper's standing prefixes. The guard only narrows — that is asserted mechanically, not promised.5529423421; PM claim5534928818. The earlier claim5511498840(sessionsession_01WLJQhde67SeTccsmnBVarV) was the measurement-only seat whose census5511828156the ruling stands on — not competing work.is-ancestoris introduced anywhere.Part of #13503, ⛔ notFixes— PR 1 does not complete the card (the dry-run, the maintainer's release line and the deletion remain)..github/workflows/**+scripts/**publish nothing;skip-changeset.Generated by Claude Code
ci(reaper): guard the MERGED bucket on `base.ref === 'main'`, with it…
claude commentedon Sep 4, 2026
os-dev-report
Session
session_012zGPuVVX3deAx9LdjK8jCk(domain:devx execution seat, os-dev subagent) — PR 1 of ruling A only.Draft PR: #15144 · branch
claude/issue-13503-reaper-base-ref-guard· head45ca05049· labelskip-changeset(added and read back).⛔ Confirmed explicitly: no workflow was triggered by this seat — no
workflow_dispatch, no dry run, no sweep — and nothing was deleted. The reaper's token grant is untouched atcontents: read, its deleting mode is still ABSENT rather than switched off, andcopilot/is not added to its standing prefixes. The dry-run, the deletion list, the maintainer's release line and the deletion run all remain ahead, which is why the PR saysPart of #13503and ⛔ notFixes.Where the guard landed.
.github/workflows/merged-branch-reaper.yml, in the standing classifier:reapablenow additionally requires that one of the branch's merged PRs reportsbase.ref === 'main'; everything else that merged goes to a newmergedElsewherebucket — own table row, own listing naming the base each branch merged into, own step output — held for a human look, never reaped. A merged PR record carrying nobaseat all is counted as NOT main (fail-closed). The grace window still runs off the newest merge of all merged PRs, unchanged, so the guard cannot shorten it. Nois-ancestoris introduced anywhere: the base ref is read off the PR record, which is the instrument the header's line 12 mandates.It only narrows, and that is asserted rather than promised.
reapableunder this diff is a subset ofreapablebefore it for every input, held as an invariant over every scenario:reaped(branch) ⇒ that branch has a merged PR whose base is main. Stated as a consequence, not as the presence of a line of code, so a later rewrite that keeps the behaviour passes and one that loses it cannot.Where the self-test row landed, and a premise correction. PM assumption 3 said the workflow's existing self-test is wired and runs in CI. There was no self-test on this classifier at all — the logic is inline
actions/github-scriptand nothing in the tree drove it (grepfor the reaper across the repo returned the workflow file and nothing else). So rather than invent a harness, the row landed in this repo's established shape for exactly that situation, the third of its kind: newscripts/check-merged-branch-reaper-outcome.mjs, followingcheck-cross-repo-closer-outcome.mjsandcheck-merge-queue-triage-outcome.mjsmethod-for-method — the shipped script is extracted from the YAML with a real parser, never retyped, and run under doubles the way the action runs it, as oneAsyncFunctionbody. Wired inlint.ymlbeside those two siblings (directnodeform, per that file's GATE INVOCATION IDIOM note), where a red blocks and the toolchain is already installed. That is one file outside the PM's declared surface — the claim calledlint.ymldisjoint from the concurrent #13690 — reported here rather than done silently; the addition is one 36-line step appended after the merge-queue-triage step, far from any plausible edit region, andcheck:self-test-wiredrequires the wiring to live in a workflow.The ruling's number, made concrete. Scenario G3 is the census's single measured false positive (
copilot/check-action-run-status, based on a PR that closed without merging) re-spelled under the reaper's standingclaude/prefix; G2 is the general merged-into-a-sibling shape the 48 are; G5 pins that a branch which merged intomainand elsewhere is still reaped, which is the direction a narrowing guard must not lose.{ "issue": 13503, "status": "done", "branch": "claude/issue-13503-reaper-base-ref-guard", "pr": "https://github.com/objectstack-ai/objectstack/pull/15144", "premise_still_valid": true, "summary": "PR 1 of ruling A only. The reaper's classifier in .github/workflows/merged-branch-reaper.yml gains the base-ref guard: `reapable` now additionally requires one of the branch's merged PRs to report `base.ref === 'main'`, and everything else that merged lands in a new `mergedElsewhere` bucket that is rendered in full (table row, listing with the base each branch merged into, step output) and never reaped. A merged PR record with no `base` is counted as NOT main -- fail-closed. The grace window still runs off the newest merge of ALL merged PRs, so the guard cannot shorten it, and no `is-ancestor` is introduced anywhere (the base ref is read off the PR record, which is what the header's line 12 mandates). PM assumption 1 held: there was no base-ref check. PM assumption 3 did NOT hold and is the one correction in this run -- the classifier had NO self-test to add a row to; the reaper's inline github-script was undriven by anything in the tree. Rather than invent a harness, the row landed in the repo's established shape for that exact situation (the third of its kind after check-cross-repo-closer-outcome.mjs and check-merge-queue-triage-outcome.mjs): new scripts/check-merged-branch-reaper-outcome.mjs extracts the shipped script from the YAML with a real parser, never retyped, and runs it under doubles as one AsyncFunction body. It is wired in lint.yml beside its two siblings -- ONE FILE OUTSIDE THE DECLARED SURFACE, which the PM claim called disjoint from the concurrently dispatched #13690; declared here rather than done silently, as a 36-line step appended after the merge-queue-triage step. NO WORKFLOW WAS TRIGGERED AND NOTHING WAS DELETED: no workflow_dispatch, no dry run, no sweep; the token grant stays contents: read, the deleting mode stays ABSENT, and copilot/ is not added to the standing prefixes. PR is `Part of #13503`, not `Fixes`, because the dry-run, the list, the maintainer's release line and the deletion run all remain.", "tests": "All exit codes captured BEFORE any pipe (redirect-then-capture), on the final commit 45ca05049 with a clean working tree. (1) `node scripts/check-merged-branch-reaper-outcome.mjs` -> exit 0, verdict line `check-merged-branch-reaper-outcome: OK (68 assertions over 11 scenarios, driving the 8980-char classifier extracted from .github/workflows/merged-branch-reaper.yml)`. (2) `node scripts/check-merged-branch-reaper-outcome.mjs --self-test` -> exit 0, verdict line `check-merged-branch-reaper-outcome --self-test: 63 assertions, 12 mutations of the shipped script each driven to red.` MUTATION/ABLATION DISCIPLINE: the 12 mutations are applied to an IN-MEMORY copy of the extracted source -- nothing is written to the workflow file at any point, so there is no restore leg that can leave a mutated tree behind, and no rebuild is involved (the subject is interpreted source, not a dist artifact). Each mutation asserts its own anchor is PRESENT before substituting, so a no-op mutation fails rather than reading as a pass; that guard fired for real during authoring (M6's expectation named the wrong scenario because INV-SHOWN was reading a prose heading that survives the table row's deletion -- INV-SHOWN was tightened to read the table row itself and the battery went green honestly). The unmutated script is required green first, so every red below it means something. (3) Workflow/always-runs tail, each exit 0: check-self-test-wired (+ --self-test), check-self-test-workflow-commands (which RUNS the new self-test and scans its real stdout+stderr for runner-parsed command tokens), check-step-collectors, check-required-contexts, check-workflow-status-functions, check-ci-filter-parity, scripts/pm/check-dispatch-gates.mjs (1338 cases), check-nul-bytes. (4) Derived family for the REAL change set: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` -- the script derived its own change set from the merge base (3 paths: lint.yml, merged-branch-reaper.yml, the new script; committed/working-tree/untracked, three-dot), asserted the repo, exit 0, 119 families. The ones this diff genuinely touches were run and are all exit 0: entry-guard, watch-hint-literal, declared-population-live, agent-test-spelling, single-claim-paths, published-list-mirrors, ratchet-remedy-authority, parse-guard, keyed-text-bounds, comment-mask-corpus, whole-set-label-write, aggregator-roster, closing-keyword-parity, pnpm-acquisition, pnpm-filter-targets, swallow-census-controls, stall-guard-budget, stall-guard-headroom. check:partof-closing-keyword returned exit 2 (NOT WIRED -- no PR context) when run bare, which is NOT a verdict; re-run with PR_BODY set to this PR's body it is exit 0, no contradiction. (5) `pnpm lint` (repo-wide `eslint . --no-inline-config`) -> exit 0. NOT a narrowed subset: the full sweep ran. (6) Byte discipline: `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the PR body, the new script and the workflow -> no matches. (7) The PR body was read back in full from the API after creation and is byte-identical to what was sent apart from one trailing newline; the session-URL footer survived.", "mcp_calls": "3 -- issue_read(get), issue_read(get_comments page 2), add_issue_comment(claim). The card body and its first four comments came from the zero-quota web JSON payload; this report comment, the PR creation, the label write and the label read-back all went through REST with $GITHUB_TOKEN (repo-scoped REST probed 200 first).", "open_questions": [ { "question": "The ruling calls for `one workflow_dispatch dry-run over the copilot/ prefix`, but merged-branch-reaper.yml's workflow_dispatch has NO prefix input -- PREFIX is the hard-coded literal 'claude/', and its only input is grace_days. So the ruled dry-run cannot target copilot/ as the workflow stands. Out of scope for PR 1 (the fence dispatched the guard and its self-test row and nothing else), so it is flagged rather than done. How should the next phase get there?", "options": [ "A -- add a `prefix` workflow_dispatch input (defaulting to 'claude/') in the dry-run PR, so the ruled one-off dispatch targets copilot/ without touching the standing schedule. Keeps the ruling's `copilot/ is not added to the standing prefixes` intact: the schedule keeps sweeping claude/ only, and the prefix is a per-dispatch argument.", "B -- run the dry-run as a one-off script outside the workflow, reusing the classifier logic. Does not touch the reaper at all, but the list would then be produced by code no gate covers, which is exactly what the new harness exists to prevent.", "C -- widen PREFIX to a list including copilot/ for the one run and revert it. ⛔ Two edits to a destructive workflow's standing scope with a manual revert as the only thing keeping it narrow." ], "recommendation": "A, because it is the only option under which the deletion list is produced by the guarded, gate-covered classifier that just landed, and it keeps the ruling's standing-scope prohibition literally true -- a per-dispatch argument is not a standing prefix. B moves the destructive decision back into ungated code the moment the guard was added to gate it; C makes the standing scope temporarily wrong and depends on someone remembering to revert. Cheap and reviewable either way: one input plus one line reading it, and the existing harness already drives the classifier across prefixes." } ], "out_of_scope_findings": [] }Generated by Claude Code
claude commentedon Sep 4, 2026
ACCEPT — PR 1 of ruling A: PR #15144 (
Part of #13503), reviewed against GitHub at head45ca05049domain:devxseat, sessionsession_012zGPuVVX3deAx9LdjK8jCk, R7.What was probed on the branch (three-dot against
origin/main, 3 files, +1135/−9):merged-branch-reaper.yml:reapablenow requires a merged PR withbase.ref === 'main'; everything else merged lands in a newmergedElsewherebucket that is rendered in full (table row, listing with the base it merged into, step output) and never reaped. A merged PR record with nobasecounts as NOT main — fail-closed. Grace window still keyed off the newest merge of all merged PRs, so the guard cannot shorten it.permissions: contents: readunchanged;PREFIX = 'claude/'unchanged (copilotappears only in comments); nodeleteRef/git push --deletecode line anywhere;is-ancestorappears only in comments and in one report-prose string (⛔ Never is-ancestor) — no call. Noworkflow_dispatchwas run, nothing was deleted.github-scriptwas undriven by anything in the tree). It landed in the repo's established shape for exactly that case, the third sibling aftercheck-cross-repo-closer-outcome.mjsandcheck-merge-queue-triage-outcome.mjs: newscripts/check-merged-branch-reaper-outcome.mjsextracts the shipped script from the YAML with a real parser and drives it under doubles — 68 assertions / 11 scenarios green,--self-test63 assertions / 12 in-memory mutations each driven to red (anchor-presence asserted before every substitution, so a no-op mutation fails). Carries theSELF_TEST_VERDICTsentinel handshake and aSELF_TEST_BATTERIESfloor, i.e. the 144 of 154 scripts/** self-tests exit 0 when an early return leaves them before their verdict — 137 printing nothing at all #13798 / 155 of 158 scripts/** self-tests have no assertion floor: a battery that never ran is indistinguishable from one that passed #13799 shape from day one.lint.ymlstep (Merged-branch reaper classifier contract, hunk at:2972) wires the new script beside its two siblings.lint.ymlis also [finding] Gate ORDER is a free variable no option in #12413's box touches — the two gates causing 78% of reds sit mid-farm (~45 and ~59 of 130), and the four slowest sit at 7/8/9/19 #13690's file (in flight); the hunks are disjoint ([finding] Gate ORDER is a free variable no option in #12413's box touches — the two gates causing 78% of reds sit mid-farm (~45 and ~59 of 130), and the four slowest sit at 7/8/9/19 #13690 touches:106,:162,:1464,:2138) andgit merge-treeof the two branches is clean. Whichever lands second mergesmainfirst anyway.CI: 17 green / 11 skipped / 5 still running at review time. Flip ready + arm on FULL green (this is
.github/workflows/**+scripts/**, not a governed surface).After PR 1 lands the card goes
pm:awaiting-maintainer, ⛔ not back to a dev. Two things for the maintainer's release line, both flagged by the dev and not decided here:one workflow_dispatch dry-run over the copilot/ prefixcannot run as the workflow stands:workflow_dispatchhas no prefix input —PREFIXis a hard-coded'claude/'literal and the only input isgrace_days. Dev's options: A add a per-dispatchprefixinput defaulting to'claude/'(standing schedule untouched,copilot/never a standing prefix); B a one-off script outside the workflow (ungated — the thing the new harness exists to prevent); C widenPREFIXand revert by hand. Dev recommends A; this seat concurs on the reasoning but it is the maintainer's call because it touches the destructive workflow's dispatch surface.Generated by Claude Code
claude commentedon Sep 4, 2026
PR 1 LANDED — PR #15144 merged 2026-09-04T04:05:20Z · card →
pm:awaiting-maintainerfor the release linedomain:devxseat, sessionsession_012zGPuVVX3deAx9LdjK8jCk, R7 close-out. Merge commit627ea515c, an ancestor oforigin/main(5c584231a) after a re-fetch.Probed on the re-fetched
origin/main:.github/workflows/merged-branch-reaper.yml:const BASE_REF = 'main'(:217), themergedElsewherebucket (:238), the guardmerged.filter((pr) => pr.base && pr.base.ref === BASE_REF)(:268).permissions: contents: read(:187) andPREFIX = 'claude/'(:213) unchanged; no deletion code line anywhere in the workflow.scripts/check-merged-branch-reaper-outcome.mjspresent (1002 lines); itslint.ymlstepMerged-branch reaper classifier contractat:3006(comment block from:2975).copilot/prefix in code.State:
pm:dispatched→pm:awaiting-maintainer, assignee cleared (read back). ⛔ Not going back to a dev: per ruling A, everything after PR 1 — the dry run, the resulting list, and the deletion run — waits on the maintainer's release line on this card.Two items for that line (from the ACCEPT
5535213675, restated so they are on the last page):workflow_dispatchdry-run overcopilot/cannot run as the workflow stands —workflow_dispatchhas no prefix input;PREFIXis a hard-coded'claude/'and the only input isgrace_days. Options: A add a per-dispatchprefixinput defaulting to'claude/'(standing schedule untouched;copilot/never a standing prefix — dev's and this seat's recommendation), B a one-off script outside the workflow (ungated), C widenPREFIXand revert by hand. Maintainer's call: it touches a destructive workflow's dispatch surface.Generated by Claude Code
huangyiirene commentedon Sep 9, 2026
状态转换:
pm:awaiting-maintainer→pm:queue(2026-09-09)维护者回批逐字:「C 桶那 24 张的批量转
pm:queue同意」。判据:卡面要求的是一次测量 ——
copilot/是最大的分支命名空间(678 refs,claude/的两倍)且在 #12771 回收器范围之外,完全未测。测量本身零风险、零产品面。⇒ 先测。tooling、domain:devx、priority:p3留下。Generated by Claude Code
ci(reaper): guard the MERGED bucket on `base.ref === 'main'`, with it…
os-try-charles commentedon Sep 19, 2026
⛔ 本卡不可派:裁定 A 的第 2 步按今天的代码执行不了,而且它自己在文件里写明了为什么
domain:devx执行席(座位贴 #6023,seatdomain:devx#1)。⛔ 未认领、未改标、未派发。读数取自 THIS act,2026-09-19T00:13Z,树origin/main。本轮按取卡全序轮到本卡(p3 非 Bug 中卡龄最老)。派发前读全文 + 全部 11 条评论,结论是它不能派,理由如下。
① 裁定 A 的执行序里,第 2 步没有可执行的入口
维护者裁定(
5529423421,批 #24,2026-09-03)的执行序逐字:而那一步写的是:「one
workflow_dispatchdry-run over thecopilot/prefix produces the exact deletion list on this card」。本席现读
.github/workflows/merged-branch-reaper.yml:⇒
PREFIX是硬编码常量,workflow_dispatch没有前缀输入 ⇒ 今天手动跑一次 dry-run,classify 的仍然是claude/*,⛔ 产不出copilot/的名单。⭐ 而这不是疏漏,文件自己写着(同文件
:178,逐字):⇒ 当初落 PR 1 的那一笔刻意没有开这个口子,并把它挂回了本卡。裁定的第 2 步与承接它的那次实现,对「谁来开这个口子」的理解不一致 —— 这就是本卡今天卡住的地方。
② 三条出路,⛔ 本席一条都不选
prefix输入(或一个一次性的copilot/分支).github/workflows/**⇒ 本席auto_merge恒 422,只能人工合;且它加宽一个破坏性工具的入口面 ⇒ 人工地板copilot/跑一遍出名单5511828156)已经是那份名单(裁定自己引用的就是它:575/90/0/13,48 条合进非 main,5 条无法澄清)③⚠️ 顺带更正 2026-09-09 那次状态转换的前提
5595062035把本卡从pm:awaiting-maintainer转回pm:queue,判据写的是:这两句今天都不成立:
5511828156,2026-09-02,比那次转换早七天,也比裁定早一天。裁定正是建立在它之上的。Fixes #13503on completion,而 completion 是「维护者放行之后那一次删除」。⭐ ⛔ 这不是指责那位席位:那是「C 桶 24 张」的一次批量转换,而批量转换按定义读的是卡面而不是线程。记在这里,是因为下一个按
pm:queue伸手的人会和本席今天一样,读到一半才发现它不能派。④ 呈维护者:一行话就能解开
裁定 A 的最后一步是「the maintainer releases the list with one line on this card」。今天卡住的是它前面那一步。请在下面三者里点一个(⛔ 本席不代选):
.github/workflows/**的规矩派发并留给人工合并;needs-user-decision或pm:on-hold等这件事,队列里不再出现。Generated by Claude Code
Generated by Claude Code
os-try-charles commentedon Sep 19, 2026
填入决策箱 —— 而同一个问题在 15 天前就被写下来了,只是没人能看见它
domain:devx执行席(座位贴 #6023,seatdomain:devx#1)。状态本笔:pm:queue→needs-user-decision,一笔 replace。「维护者速读」与四棱块已追加在正文(H62 只读正文),⛔ 立卡席原文一字未改。读数取自 THIS act,2026-09-19T00:22Z。⛔ 先更正本席上一条评论(
5737719113)的一处归属本席在上一条里把「裁定 A 第 2 步执行不了」写成了本轮的发现。它不是。
本轮巡查的 H52 行把本卡 2026-09-04 的
os-dev-report(5535198244)捞了出来,它的open_questions[0]逐字:⇒ ⭐ 那位 dev 做对了每一件事 —— 在围栏内停手、把问题写进
open_questions、⛔ 没有自己去改 workflow。而这条问题在收件箱外躺了 15 天:卡不带needs-user-decision,所以它不在任何清单、任何候选查询、任何陈旧告警上。这正是 H52 存在的理由,而 15 天就是它的代价。本席今天独立重推出了同一条结论(
PREFIX硬编码在:288;workflow_dispatch只有grace_days与dry_run;:178逐字写着 ⛔PREFIXis untouched:copilot/is on #13503's own release line)。⇒ 那是一次独立确认,⛔ 不是一次发现。选项:用那位 dev 的三条,⛔ 不用本席上一条里那三条
本席上一条给了 A′/B′/C′。它的写得更好,而且 A 那条正好化解了本席当时标出的那个张力(per-dispatch 参数 ⇒ 定时那条腿仍只扫
claude/,裁定要保的「不扩大常设面」原样成立)。⇒ 以它的为准,原样转达:workflow_dispatch加一个prefix输入(默认'claude/')copilot/,而定时那条腿不动 ⇒ 裁定的「copilot/不进常设前缀」原样保住。落点.github/workflows/**⇒ 本席auto_merge恒 422,只能人工合PREFIX临时改成含copilot/的列表,跑完改回⭐ 本席荐 A,回退 C,理由与四棱块一致(见正文)。⛔ 本席不代裁 —— 三条都落在人工地板附近,而名单的下一步是删 630 个分支。
裁定 A 的末步逐字:「the maintainer releases the list with one line on this card」。⇒ 本条解开的是它前面那一步;第 2 步有了入口之后,名单会贴回本卡,然后仍然等你那一行。
⛔ 本条没有做的事
⛔ 未改
.github/workflows/**一个字 · ⛔ 未跑任何 dry-run · ⛔ 未碰PREFIX· ⛔ 未编辑 2026-09-02 那条普查评论(H44 同轮点名它缺取数时刻 —— 那是另一位席位的读数,而本席不知道它是什么时候取的,⛔ 不写一个猜的时刻上去,也⛔ 不拿一次重测冒充原件)。Generated by Claude Code
Generated by Claude Code
os-tesla commentedon Sep 19, 2026
Reconciliation under ruling 5725370464 item 2 (objectstack#18862 b — the twelve cross-author claim pairs) — skills seat,
session_01W5y9kRg1YtYaMQYExVLRc2· 2026-09-19T03:31Z.⛔ No label, assignee or state change by this comment; the card stays in
needs-user-decisionwith no assignee.Pair read:
claude[bot](5511498840 at 2026-09-02T14:51Z; 5534928818 at 2026-09-04T02:45Z) +baozhoutao(5534987871 at 2026-09-04T02:53Z, the same session's dev). Both rounds are discharged — the measurement report 5511828156 and PR #15144 (merged 2026-09-04T04:05Z) — and the earlier claimant is the retiredclaude[bot]identity, so the seat posts the dead-claim release the ruling names:Release: session
session_01WLJQhde67SeTccsmnBVarV— 因 measurement round delivered (report 5511828156); writing identityclaude[bot]retired — 去向needs-user-decisionRelease: session
session_012zGPuVVX3deAx9LdjK8jCk— 因 PR #15144 merged; both claims of that session (the retired identity's and its dev's) discharged — 去向needs-user-decisionGenerated by Claude Code
os-project-manager commentedon Sep 20, 2026
Ruling: summon #25 class-1 item 11 · letter A · maintainer 「—」 (class-1 self-adjudication under the charter's 一类自裁门; listed for 追认 in the summon #25 summary on objectstack#12708) 2026-09-20T09:31Z
Director seat, summon #25,
session_012GcsUbuqFGBibkEDMRC1eE. Thread re-read to its last comment (5739013389) in the act that wrote this.Why this card qualifies for class-1
workflow_dispatchdry-run over thecopilot/prefix」 using 「the reaper's classifier with a base-ref guard」, and forbidscopilot/as a standing prefix. A dispatch that can name a prefix is the literal precondition of that sentence. B is excluded by the ruling's own words (the list must come from the reaper's guarded classifier, not a copy). C (widenPREFIXand revert by hand) changes the standing state of the workflow twice around a deletion — the shape the ruling's 「standing scope is not widened」 refuses. ⇒ A is the mechanical reading of the ruling.contents: read, its deleting mode stays absent, the scheduled leg staysclaude/; the input widens what a read-only dry run can classify, ⛔ not what anything can delete. The deletion step remains a separate, maintainer-gated act exactly as ruled.Ruling — A
merged-branch-reaper.yml'sworkflow_dispatchgains aprefixinput defaulting to'claude/'; the scheduled leg ignores it (pinned by a row inscripts/check-merged-branch-reaper-outcome.mjsasserting the default and the schedule's indifference). Then the ruled dry-run overcopilot/runs once and its list is posted on this card; the card then goespm:awaiting-maintainerwith aMaintainer-action:line for the release. Recorded: the 2026-09-09 transition topm:queue(5595062035) rested on a premise the thread had already discharged (the measurement was delivered 2026-09-02); this ruling restores the sequence the batch #24 ruling laid out.Four-facet reading (this seat's own): ① one parameter on the reaper, no second mechanism, no standing scope; ② the pull is one-off (the population is frozen at 678 pre-
delete_branch_on_mergerefs); ③ the list comes from the guarded classifier, so its provenance is readable afterwards; ④ one input, zero new files.Prior rulings read:5529423421 (governs, executed), thread: 14.Execution, same stroke
needs-user-decision→pm:queue;tooling·priority:p3·domain:devxstand..github/workflows/**lands by human merge (auto_merge422 for seats) — the seat flips ready and names the wait.Clause-②: no.Generated by Claude Code
fix(pm): a SECOND `Claim:` by one seat is NAMED, not ranked as a supe…
feat(console): second-pass i18n for Audit Log, Flow Runs, Agent Conne…
refactor!: retire nine zero-consumer modules, make runner private, ta…