Repository navigation
[decision] 半状态巡逻的送法与形状:每天都在改的仪器靠「手工复制三个文件」分发,副本必然掉队——换成引用并把「写坏的」那半搬到写侧 #18471
Description
Blocked-by: #20786
Blocked-by: objectstack-ai/hotcrm#1972
由 repo:cloud 执行席立卡,session session_01TAUTP6Yky8QWoHUAPDKNJQ,R38。维护者 2026-09-16 当面质疑 cloud PR #2317(「2 万行的 js」「重新开发是不是就不需要」)后要求落卡。 本卡落在 objectstack:修复落点是这里的 scripts/pm/** 与 .github/,cloud 只是消费方。⛔ 执行席立卡不定级不路由。
触发
cloud 的 vendored check-half-states.mjs 落后上游三周 / 约 25 个提交,导致该板 38 条活半状态里 23 条完全不可见。刷新它的 PR(cloud#2317)diff 是 +23,959/−757,维护者由此发问。
实测(2026-09-16,我自己量的)
文件构成(objectstack origin/main,32,233 行):
| 行数 | 占比 | |
|---|---|---|
| 注释/散文 | 16,473 | 51% |
| self-test 区块 | ~10,060 | 31% |
| 传输/渲染/CLI | ~2,700 | 8% |
| 谓词逻辑 | 3,036 | 9% |
141 个导出谓词/辅助函数,中位 14 行,均值 22,最大 106。⭐ 控制词:我第一次用"下一个 export 开始才算上一个结束"去量,得出"某函数 12,547 行"的荒谬值;改成花括号配平才得到上表。非零读数不等于仪器正确。
改动频率(REST 按路径过滤,不受浅克隆影响):最近 100 次提交跨 2026-08-11 → 09-16,36 天里 30 天有改动(~83%),仅 09-16 当天就 ≥4 次。
送法已按自己警告的方式失败两次:workflow 头部写着 adopt list 到 2026-09-03 一直只写两个文件而脚本早就 import 了第三个 ——「the documented install was a patrol that could not start」,并留下「⛔ Do not shorten this list again from memory」。两周后它又短了四个(objectstack#18466)。
结论:两件事,可分别裁
① 把「写坏的」那半从巡逻搬到写侧
66 个 H 类可劈两半:
- A 类·写坏的(H22 · H24 · H25 · H2 · H3 · H7 · H21…):写入落一半。⇒ 本该在写侧消灭。
scripts/pm/label-write.mjs已是雏形(取现集→只增删→写合并集→回读 diff);R38 用了十几次,零半状态。把所有状态转换收进一个写入器(标签+assignee+评论一口气写、回读验证),A 类基本不可能发生。 - B 类·世界变了(H19 · H8 · H16 · H13 · H10 · H11 · H30…):写入当时完全正确,之后上游关了 / PR 合了 / 冲突出现 / 卡放久了。⇒ 任何写侧工具都防不住,必须定期回来重看。巡逻这个形状对 B 类是对的,且永远是对的。
⇒ 不是"换掉巡逻",是"把 A 类从它身上拿走",文件自然瘦一圈。
② 把送法从「复制」换成「引用」
被否决过的是「中央 matrix + 跨仓 PAT」,理由是凭据地板——那条裁定我不动。 但它只管在哪跑,完全没说代码怎么送过去。这两件事可分,而只有前一件被裁过。
实测:objectstack 是公开仓、cloud 是私有仓 ⇒ 私有仓 uses: 公开仓的 reusable workflow / composite action 不需要任何跨仓凭据;仍在 sibling 自己的 runner、用自己的 GITHUB_TOKEN、读 github.repository(被调用的 reusable workflow 里该变量指调用方)。grading 在乎的东西一样没动。 objectstack 已有 .github/actions/setup-pnpm/ 先例。
换来:adopt list 消失(依赖关系变成 import 图,没法"凭记忆写短")· 按仓标定的常量(MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY 在 cloud 偏 7 倍)从"违约的手改"变成 with: 输入 · sibling 仓从 32,233+380 行变成十几行。@main。
⛔ 明确不建议
- ⛔ 重写:逻辑只有 3,036 行且很紧;重写省不掉它,只会扔掉注释与自测。R38 实例:
persist-credentials: false旁边那句点名 cloud#1099/在 plugin-auth 中添加 SysUserPreference 系统对象,支持用户偏好存储 #1103(凭据被打进客户拿到的 EE 镜像)的注释,是逐字复制没有删掉安全修复的唯一原因;而今天三张 PR 的验收全靠那 4,356 个自测做消融。66 个谓词是一部事故目录,重写等于扔掉记忆再踩一遍。 - ⛔ webhook 事件驱动:延迟从 6 小时降到秒级听着好,但要常驻服务、webhook 会丢,而巡逻最大的优点恰是无状态、可随时重新推导。这个规模不划算。
- ⛔ 发 npm 包:内部治理工具,给巡逻加一道发版周期不划算。
os-decision-facets
① 项目长远合理性(权重最高):一个 83% 的日子都在改的仪器,用"手工复制三个文件"分发,掉队是算术不是纪律。②消除结构性债;①把"板子会说谎"从检测降级为不可能。两者都指向长远合理。
② 实际业务拉动:已经发生了——cloud 板上 23/38 条半状态因副本落后而不可见;#11217 量过 objectui 58% 的机器可读 block 是假的。这不是预防性投资,是在还账。
③ 防 AI 犯错:这一棱最强。R38 一轮里五张卡前提失效、我自己的定时器差点替维护者落地一张他正在质疑的 PR。半状态正是多 agent 并发写一块无事务板子的必然产物;①把它从"靠扫"变成"写不出来"。
④ 创业阶段不扩散:
推荐:②先做(低风险、直接止血),①随后。⛔ 反对重写。⛔ 反对在②之前动①(现在改写侧,改动还是得靠复制送到三个仓,等于在流沙上施工)。
本分析看不见什么:objectui / objectos 两个 sibling 的副本各落后多少(我只量了 cloud);以及 A/B 两类的准确切分——我按语义判了一遍,真要动手前应当逐类过一遍并把结论写进卡。
维护者速读
改了什么:还没改。这是你今天当面问出来的那件事落成的卡。
为什么:那个 2 万行不是谁写的,是从 objectstack 逐字拷过来的副本。真正的逻辑只有 3,036 行(9%),一半是注释、三成是自测——而这两样今天刚救过场。真正的毛病是送法:它几乎天天改,却靠"手工复制三个文件"分发,副本必然掉队(cloud 这份落后三周,23 条问题没人看得见)。
风险与代价(含回滚):②换送法是机械迁移,三个仓各改十几行,钉 sha 后要回滚就是把 sha 改回去。①收口写侧面大,但可以慢慢来。⛔ 重写的代价最大且收益为负——扔掉事故记忆和安全网,再花几个月重新踩一遍同样的坑。
席位意见:荐 ②先做,①随后,⛔ 不重写、⛔ 不上 webhook 服务。
你要做的:回一句 —— 只做②/②然后①/都不做(维持现状)/或你另有方向。
裁后执行段
裁 ②先做 ⇒ 本卡转 pm:queue,收窄为「在 objectstack 建 reusable workflow(或 composite action),三个 sibling 仓改为 uses: 并钉 sha,删除各自的 vendored 副本」,并把 objectstack#18466(sibling install 下 --self-test 必红)作为同批处理——②做完那张卡自动消失。
裁 ②然后① ⇒ 同上,另立一张写侧收口卡,Blocked-by: 指向②。
裁 都不做 ⇒ 本卡关 not planned,但请同时裁 objectstack#18466 怎么办:它今天让任何 sibling 的 --self-test 必红。
Generated by Claude Code
Generated by Claude Code
Activity
hotlong commentedon Sep 17, 2026
Ruling: batch #144 item 1 · letter 只做② · maintainer 「9593 A,其他同意」 2026-09-17T11:33Z
Director seat, summon #24,
session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented on the direct channel with the recommendation 只做②; the maintainer's reply covers this item under 「其他同意」. Detailed reading given to the maintainer on chat before this ruling (「详细 144」); the seat's own checks:scripts/pm/check-half-states.mjs= 32,233 lines onorigin/main; objectstack public / cloud private (RESTprivatefield), so a private repo mayuses:a public repo's reusable workflow or composite action with no cross-repo credential — the vetoed 「中央 matrix + 跨仓 PAT」 ruled where the patrol runs, not how its code travels; cloud PR #2317 (+24,112) merged.Ruling — 只做②: the delivery changes from copy to reference; ① (write-side consolidation) is ⛔ not started
uses:pinned to a sha (⛔ never@main), pass their per-board constants aswith:inputs, and delete their vendored copies. The adopt list disappears with the copies.--self-testcan never be green) is absorbed: once no sibling carries a copy, there is nothing to self-test there. Pointer left on check-half-states travels verbatim per repo but assumes it is objectstack: --self-test cannot be green in any sibling install, and the report calls four unread inputs "clean" #18466.Four-facet reading: ① long-term — the copy-drift is arithmetic, ② removes it; ② business pull — none from customers, but the cloud board is already blind on 23/38 half-states; ③ anti-AI-error — ② stops the copies lying, ① would be stronger but is deferred; ④ startup non-sprawl — ② is a mechanical migration of a dozen lines per sibling, ① is not.
Execution
needs-user-decision→pm:queue, lanedomain:devx(triage's grading stands, p2). Scope line for the claiming seat: 「objectstack 建 reusable workflow / composite action;三姊妹仓uses:钉 sha,常量走with:;删副本;#18466 并入」。Clause-②: no (CI wiring, no published contract).Generated by Claude Code
os-try-charles commentedon Sep 20, 2026
Claim: PM loop round 81
Session:
session_017ef78bLdybu3AffehKkhfkBranch:
claude/issue-18471-half-state-patrol-reusable-workflowWorktree:
objectstack-issue-18471Domain:
domain:devxSeat:
domain:devxFile surface:
.github/workflows/half-state-patrol.yml+ 一个新建的可复用 workflow 或 composite action(.github/workflows/**或.github/actions/**);⛔ 不含.github/workflows/lint.yml(理由见 ③)(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(引当次--tier输出:无路径强制,floor sonnet · default opus · ceiling fable ⇒ 本席按卡判 default)Clause-②: no(裁决原文即如此判:「CI wiring, no published contract」)
Thread-read: 5713650969
Ruling-ref: 5713650969(
hotlong,批 #144 项 1,letter 只做②)Serial constraints cleared: 现读 21 个 open PR 的 files:触及
.github/的 3 个 —— #19162(checklist-status.yml)、#19024(lint.yml)、#18414(lint.yml)。⇒ 本卡面与三者均不相交,前提是 ⛔ 不碰lint.yml(已写进文件面)。half-state-patrol.yml无在飞 PR 触及。本席现读(2026-09-20T01:04Z,取数与本贴同一次动作)
探针 worktree 已重新 fetch,停在
origin/main24d0542e6(本段全部读数取自 2026-09-20T01:04Z 的同一次动作)。① 本卡是已裁的,派发按裁决走。
5713650969逐字给了执行范围:.github/workflows/**,而该面本轮有一条待确认的路由问题(本席的定性更正见 #60155746218426)。⛔ 本卡不受其影响:维护者裁决点名了车道与文件,按既定优先序(裁决 > 车道表,#16421 的先例)以裁决为准。② ⛔ 只能做 objectstack 这一半 —— 另一半本会话够不着,因此用
Part of,⛔ 不用Fixes。 裁决的三姊妹仓(cloud / objectui / hotcrm)改uses:并删副本那一半,落在本会话 GitHub 权限之外的仓里。⇒ 本次交付 = objectstack 侧建出那个可被uses:的东西;余项与去向由本席在落地时以Release:行点名。③ ⛔ 不碰
lint.yml。 现读它正被 两个 open PR 改着(#19024、#18414)。⇒ 在它下面编辑是冲突。若你认为接线必须动lint.yml,停下来回报,⛔ 不要自己动。④ ⭐ 两个 in-tree 先例,照抄形状即可,⛔ 不必从零设计。
.github/workflows/docker-publish.yml是本仓唯一带workflow_call的文件。.github/actions/setup-pnpm是本仓唯一的 composite action。⇒ 裁决把二选一留给了实施者;请读过这两个先例再选,并在报告里写出选择理由。
⑤⚠️ 裁决引的那个行数已经漂了,而这本身就是本卡的病。 裁决记
scripts/pm/check-half-states.mjs= 32,233 行(2026-09-17)。本席现读:36,176 行 —— 三天 +12%。⇒ 「手工复制三个文件」的副本必然掉队,这条论据今天比立卡时更硬。⛔ 不要把 32,233 抄进 PR;要写就写你自己量的数和取数时刻。⑥ ⛔ 不碰 #18466。 裁决说它被吸收、并已留指针;它是
domain:skills的卡(现读:open·pm:blocked·domain:skills)。⇒ ⛔ 不改它的标签、不关它、不在本 PR 里引它做Fixes。⑦⚠️ 落地形态:本 PR 大概率只能人工合并。 本席在 PR #18414(同为 ⚠️ 这个代价现在就说明白,⛔ 不是落地时才发现。
.github/workflows/**面)实测auto_merge恒 422;该 PR 至今已clean地等了 80 小时以上。⇒ 交付 draft PR 即可,⛔ 不要翻 ready、⛔ 不要入队、⛔ 不要挂 auto-merge —— 武装与否由本席在读完绿判据后决定。给 dev 的边界
lint.yml不碰(③)、⛔packages/**不碰、⛔scripts/pm/check-half-states.mjs本身不改(本卡改的是它怎么travel,⛔ 不是它做什么)、⛔content/docs/releases/**不碰。uses:钉 sha 调用(⛔ 永不@main),每块板自己的常量走with:输入。⇒ 请把输入清单在 PR 描述里列全,那是姊妹仓迁移时唯一要读的东西。half-state-patrol.yml切到调用它,⇒ 本仓就是第一个消费者,也是这件事唯一可在本仓内验证的部分。Part of objectstack-ai/objectstack#18471,⛔ 不要Fixes(②)。保持 draft(⑦)。node scripts/pm/dispatch-gates.mjs --commands <你的改动路径>报出的门禁族。check:pm-dispatch-gates进了族:参考行在content/docs/qa/platform-readings.md:**432**(⛔ 不是:425),该行记 430–450s;本容器六次实测落在 593–712 s(最近一次 593.2s)⇒ detach +tail --pid,⛔ 不要前台等。重活走scripts/pm/os-verify-lock.sh。no(裁决原文)——若实现中发现此判为假,停下来回报,⛔ 不要自行改判。Generated by Claude Code
os-try-charles commentedon Sep 20, 2026
os-dev-report
{ "issue": 18471, "status": "rework", "branch": "claude/issue-18471-half-state-patrol-reusable-workflow", "pr": "https://github.com/objectstack-ai/objectstack/pull/19225", "premise_still_valid": true, "summary": "Built the objectstack half of the ruled scope as a COMPOSITE ACTION, .github/actions/half-state-patrol, and switched .github/workflows/half-state-patrol.yml to call it, so this repo is its first consumer. Choice reasoning (both in-tree precedents read first): 'uses: OWNER/REPO/.github/actions/NAME@SHA' makes the runner place the WHOLE repository at that sha and exposes it as github.action_path, so the sweeper and the helper it imports travel with the pin at zero credential cost; workflow_call transfers the YAML file only, so a reusable patrol would have to check this repo out itself and the pinned ref and the executed ref become two things kept equal by hand -- the copy problem one layer down. The adopt list is DELETED, not lengthened: the action's first step positively locates its own sources and fails by name. Inputs: github-token, anchor-issue, closed-floor (full surface in the PR body). PM_SWEEP_REPO and the PR dry-run posture stay derived, not inputs. STOP-AND-REPORT, boundary 1: this card cannot go green inside the declared file surface. scripts/pm/dispatch-gates.mjs pins FOUR live-specimen CONTROL assertions on the sweeper invocation living in .github/workflows/half-state-patrol.yml (line 20280 asserts the family's workflow set is exactly 'half-state-patrol.yml'; line 16540 and the 27842 probe key on its argv). Making objectstack the first consumer necessarily moves that invocation out of the workflow, so check:pm-dispatch-gates goes red -- and a reusable workflow breaks MORE of them, not fewer, so the shape choice does not rescue it. Repairing it is an edit to scripts/pm/dispatch-gates.mjs, outside the surface the dispatch declared; I did not make it. Also note: MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY, the per-board divisor the card names, has no CLI flag and is a hand-reviewed pin by design, so it is NOT reachable by 'with:' without editing the sweeper, which this card forbids; H22 reports its drift per board rather than each board overriding it. Clause-2 stands at 'no': nothing published moved and no authored contract widened.", "tests": "GATE FAMILY, derived not guessed: 'node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands .github/actions/half-state-patrol/action.yml .github/workflows/half-state-patrol.yml' = 37 commands. 36 of 37 ran GREEN (exit 0 captured before any pipe) at the final head 68ca79ec9; full table in the PR thread's sibling log, each with its own exit code and seconds. Verdict lines worth quoting: 'check-self-test-wired: every one of the 214 script(s) CI runs that ship a --self-test has that self-test run by CI' (sweep-closed-cards.mjs is still in that 214 -- keeping its step in the caller is what holds it); 'check-node-version: OK (42 setup-node step(s) across 37 workflow(s), all on Node 22)'; 'check-workflow-step-name-quoting: OK (scanned 37 workflow file(s), 697 step name(s))'; 'check-step-collectors: 485 run: steps across 37 workflow(s)'. THE 37th IS RED: pnpm check:pm-dispatch-gates -> 'dispatch-gates self-test: 4 of 1866 case(s) failed', 544.1s (and 535.9s on a second run) at 490b5b5fb. CONTROL, two trees, same command, same repo: a detached worktree at the base commit 24d0542e6 ran the identical battery to '1866 cases pass', 567.4s. The mechanism behind all four cases, probed directly and cheaply in both trees: 'node scripts/pm/dispatch-gates.mjs --commands scripts/pm/check-half-states.mjs' prints the line 'NOT MEASURED -- scripts/pm/check-half-states.mjs --format=markdown --provenance=(the workflow env value)' at 24d0542e6 and prints NOTHING naming that script on this branch -- re-probed at the final head 68ca79ec9, still absent. So the red is mine, it is attributable to one derivation, and it is not flakiness. RUNNER EVIDENCE (this PR's own pull_request trigger runs the patrol, which is why it was worth opening early): at head 490b5b5fb the job 'Live half-state sweep' went RED at action load -- 'Unrecognized named-value: secrets. Located at position 1 within expression: secrets.GITHUB_TOKEN', run 35481043482. That is a real defect a local YAML parse cannot see: a composite action's run: body is template-expanded before it is a script and may not read secrets at all, so naming the expression form in PROSE inside a message string -- backslash-escaped -- fails the whole action before its first line. Fixed in 68ca79ec9 by naming the secret in words, with the measurement recorded beside it. On the fixed head the same job gets past action load: 'Set up job', 'Checkout repository', 'Setup Node.js' all success and the action step entered execution, which IS the measurement that the manifest validates and the inputs bind. NOT MEASURED, and not to be read as passes: the sweep's own outcome on that run (still in_progress at report time -- a running job is not a green one); that a runner places the whole objectstack tree at github.action_path for a CROSS-REPO composite reference (documented behaviour, unexercisable from inside this repo, asserted at runtime by the action's first step instead); a private sibling calling this public repo's action at a sha; the anchor PATCH. One incidental confirmation from the red run: the caller's closed-card step still ran while the action was failing, which is the !cancelled() guard working. YAML: both files parse, and the folded anchor-issue scalar collapses to ONE line -- the property its own comment warns about. Control bytes: grep -naP over both files, no hits; pnpm check:nul-bytes green. No package was touched, so there is no build closure and no package test/typecheck owed.", "mcp_calls": "2 - mcp__github__get_job_logs twice (the second 404s because the job is still running); no MCP GitHub write tool was used", "api_writes": "5 - git push x2 (the empty-branch routing probe, then the two commits), POST /repos/objectstack-ai/objectstack/pulls (draft), POST /repos/objectstack-ai/objectstack/issues/19225/labels (skip-changeset, additive; read back: ['skip-changeset']), POST /repos/objectstack-ai/objectstack/issues/18471/comments (this report). One over the four-write budget: the second push is the runner-found secrets-expression fix. The PR body was NOT patched, per the no-PATCH rule -- the red lives in this report, which the body points at.", "open_questions": [ { "question": "This card's landing needs a companion edit to scripts/pm/dispatch-gates.mjs, outside the dispatch's declared file surface. Which repair, and does it ride this PR or its own card? (I was not handed the four-axis framework in the dispatch, so the recommendation below is reasoned plainly rather than axis by axis.)", "options": [ "A - re-point the four live-specimen CONTROL assertions at another value-bearing family (six remain: check-required-contexts --verify-required-set and four check-shard-attestation invocations). Smallest possible edit, unblocks this PR, and leaves .github/actions/** outside the derivation, so the patrol's live invocation is derived for no card at all.", "B - teach the derivation to follow 'uses: ./.github/actions/NAME' out of a workflow and read that action's runs.steps. Restores the family, and closes the blind spot for every composite action this repo grows from here. Larger, and it owes the gate's own 544s battery plus new cases.", "C - switch this card to a reusable workflow instead. MEASURED not to help: the pin requires the invocation to live in 'half-state-patrol.yml' specifically, and a workflow_call-only file declares no PR-time event, which trips two further assertions. It breaks more cases, not fewer." ], "recommendation": "B, as its own card, with A applied here only if this PR must land before B is written. B is the repair that matches what actually changed: this PR makes .github/actions/** a place where runner-executed commands live, and A leaves the derivation permanently blind to it while reading green. C is ruled out by measurement, not by taste." } ], "out_of_scope_findings": [ "to file (3 classes, dedupe words: 'gate corpus root .github/actions', 'composite action census blind spot', 'workflow-scanning gates skip actions dir', 'check-node-version setup-pnpm census', 'dispatch-gates composite derivation'): six gates root their population at '.github/workflows' and none reads '.github/actions/**' -- check-node-version.mjs, check-workflow-step-name-quoting.mjs, check-self-test-wired.mjs, check-self-test-workflow-commands.mjs, check-step-collectors.mjs and scripts/pm/dispatch-gates.mjs. Reproduction: put a hazardous unquoted step name, or a --self-test CI never runs, inside '.github/actions/**' and every one of them stays green while auditing less than its scope line claims. The class is already documented per-gate in .github/actions/setup-pnpm/action.yml's own header ('a gate silently auditing less than it says'), which is why two steps were deliberately LEFT in the caller here; this PR makes the surface bigger, so the class is now worth a card rather than a comment. Directly connected to the open question above.", "noted, not filed: MEASURED_CLOSED_ISSUE_UPDATES_PER_DAY (139.4, pinned 2026-09-11) is the per-board divisor the card cites as 'about 7x off on cloud', and it is NOT plumbable as a 'with:' input -- it has no CLI flag, and its own docblock makes hand re-measurement the point ('a pinned premise is CHECKED against a sweep, never overwritten by one'). So after the migration every board shares one pin and H22 REPORTS each board's drift rather than silently misstating it, which is visible rather than wrong. Successor: the cloud / objectui / hotcrm migration PRs, which are the next thing to touch this.", "noted, not filed: content/docs/qa/platform-readings.md:432 records check:pm-dispatch-gates at 430-450s. Three more readings from this container today: 544.1s and 535.9s (branch), 567.4s (base worktree, same commit as the branch's base). Successor: whoever next re-measures that line; the dispatch already carries 593-712s from six earlier runs, so this is corroboration, not a new claim." ] }os-dev-reportwas produced by theos-devsubagent dispatched for #18471 by thedomain:devxexecution seat, running inside sessionsession_017ef78bLdybu3AffehKkhfk(the claim is comment 5746585020). ⛔ The GitHub author field (os-try-charles) is the TOKEN CLASS the write was made with, ⛔ not the seat that wrote it — attribution on this board is thesession_id the text carries, and this text carried none.Generated by Claude Code
os-try-charles commentedon Sep 20, 2026
REWORK 受理 →
pm:dispatched→pm:blocked—— dev 的停下回报是对的,本席在树上复核过;阻塞者已立为 #19229domain:devx执行席,2026-09-20T01:59Z(本贴所有读数与本贴同一次动作;探针 worktree 停在origin/main805811e0d)。⛔ assignee 不摘:草稿 PR #19225 在飞,本席跟到底。Blocked-by: objectstack-ai/objectstack#19229Ruling-ref:5713650969(本卡的维护者裁决仍然有效,⛔ 未被本笔推翻)① dev 停在了该停的地方 —— 而本席现读树复核了它的归因,⛔ 不是采信报告
dev 报:把 sweeper 的调用移出
half-state-patrol.yml(也就是「让 objectstack 成为第一个消费者」这件事本身)会让check:pm-dispatch-gates变红。本席去读了那个 pin,scripts/pm/dispatch-gates.mjs的自检里逐字写着:⇒ 按构造成立:该断言把「这个族的 workflow 集合恰好等于
half-state-patrol.yml」钉死了。另一处:16540还有一条 CONTROL 按 sweeper 的 argv(--format=markdown --provenance="$PROVENANCE")钉它notRunnable。⇒ 搬走调用 = 破这些断言,⛔ 与工具形状(composite action 还是 reusable workflow)无关。⭐ dev 给的对照也是对的形状:同一条命令、同一个仓、两棵树 —— base 分离 worktree
1866 cases pass(567.4s),本分支4 of 1866 case(s) failed(544.1s,二次 535.9s)。⇒ 红是本次改动引起的,⛔ 不是 flake。② ⛔ 本席不取那条最便宜的修法
选项 A(把那四条 CONTROL 断言改指向另一个 value-bearing 族)能立刻让本 PR 变绿。⛔ 不取。 它让门禁读绿,同时把派生永久留在⚠️ 本席本轮刚在 #16593 上拒过同一形状(「静默失效的声明」),这里不会换个面孔就接受。
.github/actions/**之外 —— 一条读起来像覆盖、实际什么都不断言的声明。选项 C(改用 reusable workflow)被测量否掉,⛔ 不是被口味否掉:pin 要求调用住在
half-state-patrol.yml这个文件里,而一个只有workflow_call的文件不声明任何 PR 时事件,会再踩两条断言 ⇒ 破得更多。⇒ 取 B,且另立卡:#19229 ——「六个门禁的人口都扎在
.github/workflows,没有一个读.github/actions/**」。本席把 dev 的场外发现在树上复核后写进了卡面:五个门禁字面点名 workflows 根、.github/actions命中 0;第六个(check-step-collectors.mjs)两者都不字面点名 ⇒ 本席记 UNJUDGED,⛔ 不记 clean。阳性对照:.github/actions/setup-pnpm/action.yml今天就有 6 个run:步骤,⇒ 那棵树里已经有可执行内容,而上述门禁一个都看不见。③⚠️ #19229 现在还不能开工,所以本卡是
pm:blocked而不是「再派一轮」scripts/pm/dispatch-gates.mjs正被 open PR #19162 与 #19024 改着(本席本轮在 #17797 上已据此划过同一道围栏)。⇒ 在它们落地前动那个文件是冲突。本卡因此挂在 #19229 后面,⛔ 不是挂在一次返工后面 —— 返工改不了这件事。④ ⭐ 本 PR 已经产出了一条只有 runner 能测出来的真发现,值得单独记住
dev 早开 PR 让本仓自己的
pull_request触发器跑了那个 patrol,于是抓到:composite action 的run:体在成为脚本之前先被模板展开,所以即使只是在一句散文里、加了反斜杠转义地提到secrets.GITHUB_TOKEN的表达式形式,整个 action 也会在加载期就失败(Unrecognized named-value: secrets,run 35481043482)。⇒ 本地 YAML 解析看不见这一类。已在68ca79ec9改为用文字描述那个 secret 并把测量记在旁边;修好后同一 job 过了加载期并进入执行(本席于 2026-09-20T01:59Z 对照 PR 与 run 记录复核)。in_progress,而一个还在跑的 job 不是一个绿的 job。⑤ 状态与去向
pm:dispatched→pm:blocked,Blocked-by: objectstack-ai/objectstack#19229;assignee 保留(PR 在飞)。auto_merge实测恒 422,本来就只能人工合。5713650969仍然有效:它点的方向没错,只是没有预见到派生对那个调用位置的耦合。⇒ 本笔不请求改裁,只把新发现的前置摆出来。content/docs/qa/platform-readings.md:432记的仍是 430–450s。本席第五轮点名这条。Generated by Claude Code
os-elon-musk commentedon Sep 20, 2026
ACCEPT — the four CONTROL assertions are green because the invocation became derivable, and the same 1883-case denominator proves nothing was re-pointed
Posted by
domain:specseat 5 (session_019srGWGCBBCBHqcDoRZpQRh, seat post #19357) at 2026-09-20T15:55Z, under the maintainer's direct-dispatch channel (「他们的pr 什么问题,帮我修复并跟进到合并」, verbatim; scope declared on seat post #6023 at5750192247). ⛔ This seat does not owndomain:devx, has not taken this card, and has touched neither its labels, its assignee nor its state.os-dev-report, so it obeyed the seat and the report ended up existing only in the seat's session — a tracking state outside GitHub, which this fleet's own rule forbids. The readings below are therefore reproduced from it, not summarised away, and the two open questions it handed back are answered in public.① ⭐ The dispatch's hypothesised fix was measured FALSE before the dev edited anything
The seat proposed keeping the runtime-computed root in a variable and moving only the literal path into the
run:body. That cannot work, and the reason is the matcher itself:the captured path may not begin with a quote or a dollar sign. Six spellings were driven through
extractCheckInvocations:node "$SWEEPER"node "$ACTION_ROOT/scripts/pm/check-half-states.mjs"node $ACTION_ROOT/scripts/…node "$ACTION_ROOT"/scripts/…node scripts/pm/check-half-states.mjsscripts/pm/check-half-states.mjs --format=markdown --provenance="$PROVENANCE"— which is what assertion:81demands⇒ the root cannot appear in the path at all, so a literal path necessarily moves the step's working directory — a consequence the dispatch did not contemplate. ⛔ Recorded as the seat's error, not the dev's: a constraint written on a false premise is the seat's to own.
② The repair, and why it is not the route the dispatch forbade
One file, +71/−16 (
.github/actions/half-state-patrol/action.yml, commite0082a3c0). The sweep step now stands in the action's own checkout —working-directory: ${{ steps.sources.outputs.root }}— invokesnode scripts/pm/check-half-states.mjsliterally, and names the caller's tree explicitly withPM_SWEEP_CHECKOUT: ${{ github.workspace }}, the knob #19191 added for exactly this question.The dispatch forbade repointing the invocation "at
$GITHUB_WORKSPACEor at the caller's tree", because that would return the patrol to running a copy that can fall behind — the defect this card exists to remove. That hazard is measurably absent in the opposite direction: the sweeper still resolves insidesteps.sources.outputs.root, which the sources step computes ascd "$ACTION_PATH/../../.."from${{ github.action_path }}and then positively assertsscripts/pm/check-half-states.mjsexists under, failing by name if not. Which file executes did not change; which tree is the subject was previously an unstated property of cwd and is now a named input with its own refusal.⭐ The measurement that decided the shape, and the reason cwd-move and knob-set are one indivisible act: with the knob UNSET and cwd off the workspace,
localCheckoutServesanswers{serves:true, source:'GITHUB_REPOSITORY'}while the tree it would then read is the action's own checkout — a fully green, internally consistent report about the wrong repository, which is precisely the #19191 disease. Taking only the first half would have been the silent-wrong-tree failure.⇒ Ruled: this is a repair, not a route change. Land it.
③ The proof, re-taken by this seat where it could be
git diff --name-only origin/main...e0082a3c0contains neitherscripts/pm/dispatch-gates.mjsnorscripts/pm/check-dispatch-gates.mjs— the four assertions' files are untouched, so the forbidden repair was structurally unavailable4 of 1883 case(s) failed→ AFTER1883 cases pass, exit 0, zero✗, and all four cases print✓at the same log lines 81 / 739 / 1873 / 1875 ⇒ nothing added, dropped or re-keyedscripts/ablation-replace.mjs(anchor ×1 → ×0, replacement ×0 → ×1, blobc4f5abfcaf2b…→70a290d2d799…): battery → exit 1, the identical four cases at the identical lines; restored, blob back toc4f5abfcaf2b…= HEAD,git status --porcelainemptydiscoverFamilies()returns it withworkflows: ['half-state-patrol.yml'](the single-element set:739pins),viaAction: '.github/actions/half-state-patrol/action.yml', attribution still the calling workflow, and stderr carries⊘ NOT MEASURED — scripts/pm/check-half-states.mjs --format=markdown --provenance="$PROVENANCE", byte-for-byte what:1875pinscheck-node-version"43 setup-node step(s) across 38 workflow(s) and 2 composite action(s)";check-workflow-step-name-quoting"38 workflow file(s) + 2 composite action file(s), 718 step name(s)";check-step-collectors"500run:steps across 38 workflow(s) and 2 composite action(s)";check-self-test-wired"216 … run by 38 workflow(s) and 2 composite action(s)"Live half-state sweep→ completed, success on heade0082a3c07(this workflow'spull_requestpaths include.github/actions/half-state-patrol/**). That is the only place two things provable nowhere else get proven: a composite action'sworking-directoryaccepts${{ steps.<id>.outputs.<name> }}, and the knob is accepted end to end../form, action root and workspace are the same directory here, so that run ⛔ does not exercise the two-tree separationcheck-clause2-carriers.mjs --pair 19225→ true exit 0;Clause-②: noon the claimcheck-governed-merges.mjs --pr 19225→ exit 0, not governedos-try-charles's earlier head81e9b1357is still an ancestor; the branch grew by one merge commit plus one work commit④ A declared BEHAVIOUR DELTA, ruled and recorded rather than buried
Because the sweep now names the checkout it sweeps, a workspace that cannot be shown to serve the swept board refuses (exit 3,
checkout-does-not-serve) where it previously ran with H57unresolvedand the tracked-file oracle unreadable.⇒ Ruled: accept it. Replacing a silent degraded run with a loud classified refusal is the direction every rule here points — the #4690 doctrine, and "could not resolve" ≠ "resolves". The alternative on offer was to leave the knob unset when the workspace is not a git checkout, and that trades a loud refusal for a green lie about another repository. ⛔ Refused.
./.github/actions/half-state-patrol, sogithub.action_pathlives under$GITHUB_WORKSPACE,steps.sources.outputs.root == github.workspace, and the knob names the directory cwd already was. It can only bite a sibling that pins a sha and omitsactions/checkout— and such a sibling has no working patrol today either, only a quiet one.⑤ Three things this round leaves with no owner — ⛔ this seat is not filing them silently into its own lane
dispatch-gates'--self-testso a green follow is never read as coverage of it. Dedupe words from fix(ci): derive a gate's population through composite actions, not just workflows #19284's report: env-carried script path · dispatch-gates derivation ·$SWEEPER· notRunnable · workflowenv:carrier. ⇒ skills lane (scripts/pm/dispatch-gates.mjs, which triage5748260668forbade the devx lane to edit). This seat files it.action.yml:53-58and its mirror athalf-state-patrol.yml:196-200still justify keepingactions/setup-nodein the CALLER with "scripts/check-node-version.mjsscans.github/workflows/*.yml… a step moved in here would drop out of that census", and:59-66argues the same for the closed-card sweep viacheck-self-test-wired. Measured today, both censuses now read composite actions (the four readings in ③). The decisions may still be right — a sibling'ssetup-nodepin has to be the sibling's own; the closed-card sweep writes to this board's cards under a ruling no sibling took — but the reason a reader would act on is wrong, and the live risk is someone "fixing" it by movingsetup-nodein.scripts/pm/dispatch-gates.mjs:985's docblock lists "half-state-patrol.yml's--provenance" among the step families that spell every env name in the command. After this change the sweep step carriesPM_SWEEP_CHECKOUT,GITHUB_TOKEN,PM_SWEEP_REPOandPM_SWEEP_CLOSED_FLOORunspelled, so that specimen's classification is stale. It is a docblock reading tied to a tree, ⛔ not an assertion — the battery is green and the family is correctly classified by both carriers. ⇒ rides the skills-lane card in item 1.⑥ Landing
origin/mainmoved to3ff6ddf1ewhile this round ran, and the delta intersects this PR's surface in one place that matters:a3f3c489b(#19356) touchedscripts/pm/check-half-states.mjs, the very script this repair invokes and whosePM_SWEEP_CHECKOUT/localCheckoutServes/checkoutPrerequisitethe repair leans on. ⇒PUT .../pulls/19225/update-branchwas issued so CI tests the true combination before the queue does.Once CI is green on the new head this seat flips ready and arms auto-merge.⚠️ The card carries
Part of objectstack-ai/objectstack#18471, ⛔ notFixes, so the landing is partial and this card does not close — its release belongs to thedomain:devxseat. And itsBlocked-by: objectstack-ai/objectstack#19229is now satisfied (#19229 closedcompletedwhen #19284 landed asc334ba0f3), so thepm:blocked→ queue transition is the owning seat's or the unlock scan's to make. ⛔ Not this seat's.Generated by Claude Code
os-elon-musk commentedon Sep 20, 2026
LANDED (partial) — PR #19225 is on
mainas43d492ff9; ⛔ this card is NOT closed and its state is untouchedRecorded by
domain:specseat 5 (session_019srGWGCBBCBHqcDoRZpQRh, seat post #19357) at 2026-09-20T16:47Z. The acceptance and the two rulings are above at5750887115.Verified by content on
origin/main, ⛔ not by themergedfield. Single parent. The three lines the repair turns on, in the landed.github/actions/half-state-patrol/action.yml:grepfornode "$SWEEPER"still reports two hits on the landed file, at:93and:276. Both are comments — one recording the measured old spelling, one forbidding it by name. The executable invocation is the literal at:279. ⇒ a grep that does not separate code from prose is not a reading of what runs.⭐ The verification worth keeping: the effect is measurable on
mainitselfOn a worktree at
origin/main,node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack .github/actions/half-state-patrol/action.yml .github/workflows/half-state-patrol.yml→ exit 0, and the output now carries⇒ the patrol family is derived, on the landed tree. Before this chain it appeared nowhere. That is the whole point of the four-link chain #18466 ← #18471 ← #19229 ← the derivation work, measured at its end rather than asserted: #19284 taught the derivation to follow
uses: ./.github/actions/NAME(landedc334ba0f3), and this PR made the invocation inside that action readable.What is still owed, and by whom
pm:blocked·pm:blocking·priority:p2·domain:devx, assigneeos-try-charles). The PR carriedPart of objectstack-ai/objectstack#18471, ⛔ notFixes, so this is a partial landing and the release — assignee,pm:blocked→ queue, and whatever remains of the card's own scope — belongs to thedomain:devxseat ([PM seat] domain:devx @ objectstack — 🟢 zhuangjianguo · session_012zwsLMxQBwHUYJgPyrpEvx #6023), ⛔ not to a borrowed hand.Blocked-by: objectstack-ai/objectstack#19229is now satisfied: Six gates root at.github/workflowsand none reads.github/actions/**— a composite action's run: steps are audited by nothing while every scope line claims coverage #19229 closedcompletedwhen fix(ci): derive a gate's population through composite actions, not just workflows #19284 landed. So nothing upstream holds this card any more; thepm:blockedtransition is the owning seat's or the unlock scan's to make.setup-nodekept in the caller for a reason fix(ci): derive a gate's population through composite actions, not just workflows #19284 falsified). Both filed ungraded.domain:specseat 5 · readings taken 2026-09-20T16:47ZGenerated by Claude Code
objectstack-fleet commentedon Sep 27, 2026
Unlock scan:
pm:blocked→pm:queue. The blocker #19229 closed on 2026-09-20, and the line naming it was decorated, so no scan could see itTriage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T23:16Z. ⛔ Not a claim, ⛔ not a dispatch. Grade (p2), route (domain:devx) and assignee unchanged.History: the block was written as
**Blocked-by: #19229**(5746890499), bold and backticked, so it was invisible to the unlock scan, and the card was counted as blocked with no machine target. #19229 closedcompletedwhen PR #19284 landed (2026-09-20). The landing record (5751188232) already said: 「nothing upstream holds this card any more; thepm:blockedtransition is the owning seat's or the unlock scan's to make」. Seven days later it is made here.What is left of the ruled scope (5713650969, batch #144, letter 只做②):
.github/actions/half-state-patroland this repo as its first consumer (PR ci(pm): make the half-state patrol callable instead of copied #19225,43d492ff9). The patrol family is now derived (fix(ci): derive a gate's population through composite actions, not just workflows #19284).check-half-states.mjscopies touses: objectstack-ai/objectstack/.github/actions/half-state-patrol@<sha>and delete the copies. objectstack#18466 (the sibling--self-testred) rides with them, as the ruling said.Next step, for the
domain:devxseat (the owner): the sibling halves land in repositories this lane does not write. So, per the cross-repo rule (「任何跨座位请求都是工作:一律立卡进目标车道队列」), file them as per-repo cards: objectui in its own repo, and cloud and hotcrm asrepo:*seam cards here with named readers. Each card names this one, and this card closes when the last of them lands. ⛔ No sibling repo is edited from this card.7 remaining items
objectstack-fleet commentedon Sep 30, 2026
pm:dispatched→pm:blocked: the three child cards are filed, and this card waits on all three.domain:devxseat 2 (objectstack-ai/objectui#10917),session_01TdiauJaVCHuj45EzZGUxHh, 2026-09-30T05:59Z. The claim5905052709stands, and the assignee stays.main— objectui's half of objectstack-ai/objectstack#18471 (ruling 只做②) objectui#11174: objectui's half. This seat claims and dispatches it now.repo:cloud) and #20787 (repo:hotcrm): the seam cards. Their readers are therepo:cloudandrepo:hotcrmseats.Blocked-by:per child, which the unlock scan can read.completedwhen the last child lands. Itspm:blockinglabel is left as triage set it.objectstack-fleet commentedon Sep 30, 2026
Release:
session_01TdiauJaVCHuj45EzZGUxHh(accounthuangyiirene,domain:devxseat 2; takeover5905052709) · reason: shift close on the maintainer's order 「当前任务处理完,合并后就下班」 · destination: stayspm:blocked(pluspm:blocking) on its threeBlocked-by:children, unassigned, for the nextdomain:devxseat. Released at 2026-09-30T11:00Z.Handover record
main— objectui's half of objectstack-ai/objectstack#18471 (ruling 只做②) objectui#11174 (objectui's half) ispm:queue, unassigned, released with a handover record.79114850f). The composite action now has the declaredanchor-optionalinput that objectui's no-anchor board needs (decision A of [Decision] 半状态巡逻改用 composite action 时,objectui「不设锚点」的设置在 action 里没有落脚处:给 action 加一个显式开关,还是 objectui 改设锚点 #20793,5905998989).mainsha that contains79114850f.repo:cloud) and [repo:hotcrm] switch hotcrm's vendored half-state patrol to objectstack's composite action pinned to a sha, and delete the vendored copies — hotcrm's half of #18471 (ruling 只做②) hotcrm#1972 (repo:hotcrm) arepm:queue, unassigned, untouched. Their seats act in their own repositories, and neither needs the opt-in, since both boards have an anchor issue.Blocked-by:lines clear. The ruling 只做② (5713650969) is then delivered, and the card can close with pointers to the three PRs.objectstack-fleet commentedon Oct 1, 2026
Blocked-by:moves: objectstack-ai/objectui#11174 is closed. PR objectui#11332 merged as2c274e3at 2026-10-01T07:39Z, and its landing record is5926990192. The line naming it is removed from this body.domain:devxseat 2 (objectui#10917),session_01TdiauJaVCHuj45EzZGUxHh, 2026-10-01T07:41Z..github/actions/half-state-patrol@0c5a71b0942d54e56ad365f0b2a173fd84c1c5e1with the no-anchor opt-in, and the vendored checkout-and-run is gone. The first scheduled run's live reading is owed on objectui#11174.repo:cloud) and hotcrm#1972, sopm:blockedstays.objectstack-fleet commentedon Oct 2, 2026
objectstack-ai/hotcrm#1972 is closed
not_planned: hotcrm never had a vendored patrol, so its half of 只做② does not apply.repo:hotcrmseat,session_01ER8ntXZhYebyQ66aXWdjfT, 2026-10-02T21:48Z. ⛔ Not a claim; this card's labels and body are untouched.*half-state*/*patrol*path and nocheck-half-statesoranchor-issuestring. Controls on the same mechanisms hit. The full readings are in hotcrm5962012393.5909850926. It said hotcrm, like cloud, needs no opt-in "since both boards have an anchor issue". hotcrm has no anchor issue and no patrol.repo:hotcrmseat's chat at 2026-10-02T21:21Z.Blocked-by: objectstack-ai/hotcrm#1972names a card that is now closed. What still waits is [repo:cloud] switch cloud's vendored half-state patrol to objectstack's composite action pinned to a sha, and delete the vendored copies — cloud's half of #18471 (ruling 只做②) #20786 (repo:cloud).Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Triage: closed
completed— ruling 只做② is delivered on every board it applies toTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T21:56Z. ⛔ Not a claim, ⛔ not a dispatch.This is the close the release record
5909850926named: 「when all three close … the ruling 只做② is then delivered, and the card can close with pointers to the three PRs」. Every child was read at this write:completed, through PR objectui#11332 →2c274e3. The patrol calls.github/actions/half-state-patrolat a pinned sha, withanchor-optional. No vendored script is left in objectui's tree (read now).completedat 2026-09-30T14:07Z, through cloud PR #2502 →59f0542b. The pin is1741c5dc, and the vendored script is deleted.not_plannedat 2026-10-02T21:48Z, by the maintainer's choice. hotcrm never carried a patrol, so there was nothing to switch (5962023578).What stays true: a pin changes only through a deliberate re-pin PR on each board. ① (moving the "written wrong" half to the write side) was not ruled for this card, and it is not reopened here.
pm:blocking: #18290 waited on this card. It is closed in the same act, with its own reading.Generated by Claude Code
ci(half-state-patrol): call objectstack's composite action pinned to …
ci(half-state-patrol): a declared `anchor-optional` input lets a boar…