Skip to content

Commit ad1f94e

Browse files
os-justinclaude
andauthored
docs(pm-skills): a dev container creates a remote branch it cannot delete — probe on the branch the dev keeps (#18808)
Fixes #18774 `Clause-②: no` Two files, both fact layer, both held at their ceilings: `.claude/skills/pm-dispatch/references/platform-readings.md` **466 / 466** and `.claude/skills/pm-dispatch/references/dispatch-runbook.md` **241 / 241** — net line change **0** in each, three rows added and three retired in the first, one added and one retired in the second, every added row ≤ 120 B. No ceiling raised, no ruling spent. `skip-changeset` — `.claude/**` is shipped by no package's `files[]`, so nothing published moves. ## The defect A dispatched dev's container **creates** a remote branch and **cannot delete** one on either channel, so any instruction that says "probe before you write anything" manufactures a permanent artefact: the dev reads "before writing anything" as "not on your working branch", pushes a throwaway, and then cannot remove it. The leftover is not inert — a `claude/issue-NNNN-*` head is exactly what the AGENTS.md claim pre-check greps for, so a stray branch answers "already claimed" for a card nobody is working. That failure direction is the silent one: a live card read as claimed, with no red signal anywhere. This PR does not touch the capability (proxy policy is not this repo's to decide) and does not touch the pre-check text at AGENTS.md :459–:463 (rules layer, another serial). It records the capability where branch and ref facts live, and moves the instruction so the probe lands on the branch the dev is keeping anyway. ## The capability, re-derived on this branch — ⛔ not taken from the card Both attempts were made **against this PR's own working branch**, the one that stays either way. ⚠️ No branch was created in order to fail to delete it — that is the card's whole point. **Channel ① — git, 2026-09-17T21:47Z** ```text $ git push origin --delete claude/issue-18774-probe-branch-undeletable ✓ check:commit-card-trailers: 0 commit message(s) on this push carry no card relation and no model identifier in the trailer pair. fatal: --negotiate-only needs one or more --negotiation-tip=* warning: push negotiation failed; proceeding anyway with push error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403 send-pack: unexpected disconnect while reading sideband packet fatal: the remote end hung up unexpectedly Everything up-to-date $ echo $? 1 ``` **Channel ② — REST, 2026-09-17T21:47Z** ```text $ curl -X DELETE https://api.github.com/repos/objectstack-ai/objectstack/git/refs/heads/claude/issue-18774-probe-branch-undeletable HTTP 403 {"message":"Write access to this GitHub API path is not permitted through this proxy.","documentation_url":"https://docs.anthropic.com/en/docs/claude-code/github-actions"} ``` **And the ref survived both**, read back immediately after: ```text $ git ls-remote --heads origin claude/issue-18774-probe-branch-undeletable 9846f27 refs/heads/claude/issue-18774-probe-branch-undeletable ``` ⇒ the card's premise **holds for this container**, on a third branch and in a third session. One correction to the tree's existing wording, which the rows below carry: the old row said 「容器发不出分支删除 refspec」 — the container **does** send it. The RPC is made and answered **403**; nothing is held back locally. Same outcome, different mechanism, and the mechanism is what tells a reader not to go looking for a local git config to fix. ## The census — the seat's, cited; and my re-take **Handed down by the `domain:skills` seat, 2026-09-17T21:45Z** (⛔ not retyped as mine): `git ls-remote --heads origin 'refs/heads/claude/issue-*'` answered **291** heads; against the newest 1200 PRs (back to 2026-09-05) — **26** with an open PR, **8** with a closed / merged PR whose branch was never deleted, **257** with **no PR in that window**. **My re-take, 2026-09-17T21:48Z — one `ls-remote`, zero writes:** ```text $ git ls-remote --heads origin 'refs/heads/claude/issue-*' | wc -l 291 ``` ⇒ the population **did not move** in those three minutes. The two named strays are **both present**: ```text d5e64d8 refs/heads/claude/issue-18545-formula-can-function f22c632 refs/heads/claude/issue-18734-workflow-scope-probe ``` `f22c63215` matches the sha the card names. ⛔ I did not re-take the 26 / 8 / 257 split — that is the seat's reading, used as an existing fact, and only the head count was re-checked for increment. ## ① `platform-readings.md` — the rows They land in the `## 读数陷阱` block at `:374`–`:379`, whose subject is already refs: the row above them is the zero-commit probe-branch reading, the row below is the two-read criterion and then the ⛔ against reading a `ls-remote | grep issue-` hit as a claim. ⇒ the capability, its consequence and the claim reading now sit on the criterion they qualify, instead of one being 57 rows away in the same section. **Added — three rows, verbatim, with byte counts** ```text :375 114 B - 两道皆 403:`git push --delete` 回 RPC failed,`DELETE /git/refs/heads/…` 回 not permitted,⛔ 不重试。 :376 114 B - ⇒ 探针分支永久堆在 origin 上,只有带删权的手(维护者)清得掉,本闭环内无人有该权。 :378 117 B - 「分支在」不是「卡已认领」:认领是 `Claim:` 评论,分支只是线索;291 个头里 257 个无 PR。 ``` `:374` is **amended in place**, no line bought, to carry the corrected mechanism and the replication count: ```text before - 零提交的探针分支不是在飞工作:容器发不出分支删除 refspec。 after - 零提交的探针分支不是在飞工作:容器建得出远端分支却删不掉,两会话两分支实测。 (112 B) ``` **Paid — three rows** | retired | why the tree no longer needs it as a separate line | what it merged into | |:--|:--|:--| | `:375` (pre-edit) — the git-channel row, spelling the failure as 「send-pack: unexpected disconnect,三次退避全败,同会话普通 push 正常」 with a placeholder branch token | it is a **narrative of one attempt**, and its mechanism claim is the one this PR measured wrong: the disconnect is the tail of an HTTP 403, not the signal. Its 「同会话普通 push 正常」 half is the create-vs-delete asymmetry, which is what the amended `:374` now states positively. Per this corpus's own 行文纪律 — 「原话仅限操作性判据;⛔ 实测叙事不进操作文本」 | the added `:375`, which names **both** channels and keeps the operational ⛔ | | `:376` (pre-edit) — 「⇒ 测量型派发留下的探针分支永久堆在 origin 上。」 | it states the accumulation and stops there, leaving the reader with no disposition. The one thing a reader needs next is **who can clear it**, and the answer is nobody in this loop | the added `:376`, same 「⇒」 position, same accumulation clause, plus the maintainer-only disposition | | `:431` (pre-edit) — 「分支删除被拒有第二形态:代理回 403,与既有 send-pack 断连同处置 ⇒ 不可删,⛔ 不重试。」 | the 「第二形态」 framing exists only because the fact was **discovered in two parts, 57 rows apart**. Once one row names both channels, a second row whose entire content is "and there is another one of these, treat it the same" is pure discovery history. Its two operational clauses — 不可删 and ⛔ 不重试 — are both inside the added `:375` | the added `:375` | ⛔ No re-wrap was used as currency: nothing here is two wrapped halves of one sentence pushed together to free a line. Each retirement drops content — an attempt narrative, a dangling consequence, a duplicate discovered twice. ## ② `dispatch-runbook.md` — the row **Added — one row, 119 B**, last in `## 派发词构造细则` (`:236`), directly under the measurement-first row, because a measurement-first dispatch is the shape that produced the artefact on `origin` today: ```text :236 119 B - 探针做在 dev 要留的分支上,⛔ 不用一次性分支:建得出删不掉,遗留分支被预检读成认领。 ``` **Paid — `:201` (pre-edit), 59 B** ```text - ⛔ 不让 N 个 dev 各重跑同样贵的 GraphQL 读。 ``` Why the tree no longer needs it as a separate line: it is the **rationale half of the row above it**. `:200` already carries the operational rule — 「派发那刻现取、随派发词下发的两份读数,判据同一:PM 已有的读数下发一次」 — and the prohibition it spells is stated operationally **twice more in the same block**: at `:201` (post-edit) 「⛔ 不让 dev 枚举 全 farm」 for the gate-list reading, and at `:204` (post-edit) 「当既有事实用,只复核其后的增量,⛔ 不重跑」 for the dedupe reading, which is also where the dev-side half of the contract is pointed at. Its cost clause is held by `:203` (post-edit) 「② 去重或看板读数取一次(查询式加时间戳)整批共用」. No rule leaves the corpus. ## Reserved rows — verified by content on my head Every change in this diff is at `:374` or below, so **nothing above `:374` moved by a single byte or a single line**. - `:10`–`:12` (#18469 PR-A) — byte-identical, and 362 lines above the first hunk. - PR #18775's `:29` — byte-identical: 「零 legacy status 的仓恒答空集默认值 `pending`+`total_count: 0`,⛔ 非门禁读数,门禁读 check-runs。」 - **PR #18666's band** — read against `/pulls/18666/files` at 2026-09-17T21:52Z: its only hunk on this file is `@@ -209,7 +209,7 @@`, one changed line, the `total_count` row. **On `main` and on my head the numbers are the same**: the changed line is `:212`, the hunk's context window `:209`–`:215`. Every byte in `:209`–`:215` is unchanged, so #18666 still applies cleanly. - `:432` (#18469 PR-A) — 「harness 按内容拒写:同会话派发 PR 上 PASS 拒为 `[Self-Approval]`」 is **still at `:432`** on my head: the insertion at `:378` and the retirement at `:431` cancel across it. ⚠️ The row I retired is `:431`, which is a **different row** — the same off-by-one PR #18775's body diagnosed, and the reason I checked the content rather than the number before touching anything. - Overlap re-checked: 28 open PRs at 2026-09-17T21:52Z, the same 28 the seat read at 21:46Z — no increment, and #18666 is still the only open PR touching this file. - #18798 (p1, dispatched beside this card) edits the reading-discipline rows; those live in the instrument/zero-hit block higher in `## 读数陷阱`. My rows are at `:374`–`:379`, the container-restart / probe-branch neighbourhood. No overlap. ## Ratchet before / after, and the diff `node scripts/pm/check-skill-line-ratchet.mjs` — **exit 0 both times**: | file | at `main` `9846f2763c` | at head `de12f71b8a` | |:--|:--|:--| | `platform-readings.md` | 466 lines (ceiling 466; headroom 0) | 466 lines (ceiling 466; headroom 0) | | `dispatch-runbook.md` | 241 lines (ceiling 241; headroom 0) | 241 lines (ceiling 241; headroom 0) | Widest-table-row pins are 0 for both files (neither carries a markdown table) and are unchanged. ```text $ git diff --stat 9846f27 HEAD .claude/skills/pm-dispatch/references/dispatch-runbook.md | 2 +- .claude/skills/pm-dispatch/references/platform-readings.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) ``` Five for five: 4 rows rewritten or inserted against 4 retired or replaced on `platform-readings.md` (3 added + 1 amended in place vs. 3 retired + 1 amended in place), 1 added against 1 retired on `dispatch-runbook.md`. ## ⏹️ For the MAINTAINER — the stray branches, and the one hand that can remove them Both are on `origin` right now and **no account in this loop can delete them** — the dev container and the triage seat both 403 on both channels. The two commands, to be run by a hand with delete rights: ```bash git push origin --delete claude/issue-18734-workflow-scope-probe git push origin --delete claude/issue-18545-formula-can-function ``` - `claude/issue-18734-workflow-scope-probe` — `f22c63215c6935552f9761d1ec2bd59556c7b656`, one commit ahead of `main`, no PR. This is the branch the card names; it is what makes `ls-remote --heads origin | grep issue-18734` answer **two** branches for one card. - `claude/issue-18545-formula-can-function` — `d5e64d8d9dae7b58bb7fa4aabccd95247b1fb9de`, no PR. ⛔ This PR's own branch, `claude/issue-18774-probe-branch-undeletable`, is **not** on that list — it is live work and it carries this PR. ## Gates Derived from the worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no hand-fed path list; change set 2 paths vs merge base `9846f2763`, three-dot). All 18 run, exit code captured with redirect-then-`$?`, never across a pipe: | command | exit | |:--|:--| | `node scripts/check-closing-keyword-parity.mjs` | 0 | | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | | `node scripts/check-comment-mask-corpus.mjs` | 0 | | `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 | | `node scripts/pm/check-harness-current.mjs --self-test` | 0 | | `pnpm --filter @objectstack/lint run check:doc-formula-expressions` | 0 | | `pnpm check:agent-test-spelling` | 0 | | `pnpm check:doc-authoring` | 0 | | `pnpm check:driver-memory-census` | 0 | | `pnpm check:nul-bytes` | 0 | | `pnpm check:pm-governed-merges` | 0 | | `pnpm check:pm-half-states` | 0 | | `pnpm check:pm-skill-id-lint` | 0 | | `pnpm check:pm-skill-ratchet` | 0 | | `pnpm check:refd-timer-probe` | 0 | | `pnpm check:required-contexts` | 0 | | `pnpm check:skill-frame-sync` | 0 | | `pnpm check:watch-hint-literal` | 0 | ⚠️ `check:doc-formula-expressions` answered **exit 3 — PREREQUISITE NOT MET** on its first run (「@objectstack/lint — a workspace package that is not built」), which is **not a finding and not a measurement**. The gate names its own fix; it was run: `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2` through `scripts/pm/os-verify-lock.sh` (`VERDICT command-exit 0 · held the lock 144s · waited 0s`), after which the gate answered **exit 0**. The 0 above is the second run. Reconciliation, exit codes recorded per family: ```text $ node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt Run reconciliation — 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN. ✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3). ``` Repo-wide `pnpm lint` (`eslint . --no-inline-config`) — **exit 0**, run whole, not narrowed. Control-character self-scan over both edited files, beyond `check:nul-bytes`: `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` on both paths — zero matches (grep exit 1). ⛔ Not run here and not claimed: the 53 artifact-roster families, the 11 wide-population families, the 14 pending-changeset families and the CI-measured-only families the derivation prints under their own headings — CI owns those. --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent a7bafc2 commit ad1f94e

2 files changed

Lines changed: 5 additions & 5 deletions

File tree

‎.claude/skills/pm-dispatch/references/dispatch-runbook.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@
198198
- 两种改写都在一轮内闭合:允许做 ⇒ 只做最小编辑,并在报告里单列该判断与回退方式。
199199
- 不许做 ⇒ ⛔ 不要动该文件,让门禁红着并在报告里点名该站点。
200200
- 派发那刻现取、随派发词下发的两份读数,判据同一:PM 已有的读数下发一次。
201-
- ⛔ 不让 N 个 dev 各重跑同样贵的 GraphQL 读。
202201
- ① 门禁清单单班之内即过期;全 farm 归 CI 跑一次,⛔ 不让 dev 枚举全 farm。
203202
- 点名单当天现取仍会漏,dev 对实际改动路径重取补跑。
204203
- ② 去重或看板读数取一次(查询式加时间戳)整批共用。
@@ -234,6 +233,7 @@
234233
- 结论三选一:已改、本就合规(证据)、明确不在范围(理由)。
235234
- ⛔ 静默略过:评审把没提到的面读作漏掉的面。
236235
- 测量先行卡(修复由测量结果有条件授权)⇒ 派发令写明 ⛔ 测量存在之前不写修复。
236+
- 探针做在 dev 要留的分支上,⛔ 不用一次性分支:建得出删不掉,遗留分支被预检读成认领。
237237

238238
## security-object 无判决枚举(座位贴常设 ⑦)的方法
239239

‎.claude/skills/pm-dispatch/references/platform-readings.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -371,10 +371,11 @@
371371
- 有的现场 regen 一件没跑,推送前先跑生成物门禁别赌。
372372
- ③:死在源码编辑中途 ⇒ 先读 diff 判完整性:docblock 写全动机与判据的可代跑终验后提交。
373373
- 写一半意图不明的 ⛔ 不代提交,记交接;dev 临时目录(`.os-scratch/` 一类)清掉,⛔ 不进 PR。
374-
- 零提交的探针分支不是在飞工作:容器发不出分支删除 refspec。
375-
- `git push origin --delete <b>` 回 send-pack: unexpected disconnect,三次退避全败,同会话普通 push 正常。
376-
- ⇒ 测量型派发留下的探针分支永久堆在 origin 上。
374+
- 零提交的探针分支不是在飞工作:容器建得出远端分支却删不掉,两会话两分支实测。
375+
- 两道皆 403:`git push --delete` 回 RPC failed,`DELETE /git/refs/heads/…` 回 not permitted,⛔ 不重试。
376+
- ⇒ 探针分支永久堆在 origin 上,只有带删权的手(维护者)清得掉,本闭环内无人有该权。
377377
- 判据两读:`git rev-list --count origin/main..origin/<b>` 为 0,且分支名下无 open PR。
378+
- 「分支在」不是「卡已认领」:认领是 `Claim:` 评论,分支只是线索;291 个头里 257 个无 PR。
378379
- ⛔ 不据 `ls-remote | grep issue-` 正命中回避该卡:失效方向是活卡被读成已认领,无红信号。
379380
- 会话从上下文检测不到自己的静默降档:横幅只在 UI 侧渲染,上下文零信号。
380381
- 服役档的权威读数是 `get_session`(claude-code-remote MCP,无参)的 `external_metadata.last_served_model`。
@@ -428,7 +429,6 @@
428429
- `check:pm-dispatch-gates` 单机 430–450 秒贴容器上限 ⇒ detach 加 `tail --pid=$!`;超时非读数。
429430
- `check-half-states.mjs` 连 `--help` 都跑整仓 I/O ⇒ 早读到的输出文件是空的,不是干净的。
430431
- 后台工具调用里再 `nohup … &` 会让包装器报假 `exit 0`,而真活还在跑。
431-
- 分支删除被拒有第二形态:代理回 403,与既有 send-pack 断连同处置 ⇒ 不可删,⛔ 不重试。
432432
- harness 按内容拒写:同会话派发 PR 上 PASS 拒为 `[Self-Approval]`;同通道建卡、ACCEPT 照过。
433433

434434
## 闭合关键词解析(PR 正文写侧)

0 commit comments

Comments
 (0)