docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it - #14784

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings
Sep 3, 2026
Merged

docs(pm-dispatch): timestamped readings are the first construct-shaped fix; record the remediation-shape ranking beside it#14784
os-project-manager merged 1 commit into
mainfrom
claude/issue-12911-timestamped-readings

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#12911

Governed surface (.claude/**) — DRAFT, human merge only; never flipped ready, never queued, never armed. Review requests are the dispatching seat's step, not this flight's.

The ruling, verbatim and untranslated

Director seat, summon #8, 2026-09-02 (comment 5507671383 on the card). Provenance: maintainer, live PM chat with the director seat, replying to decision batch #4 in which this card was item 3. Verbatim reply:

13973 帮我综合分析,并参考主流平台的方案,并给我解释为什么不能都用日期类型。其他同意

「其他同意」 adopts B + A. The ruled text, verbatim:

  • B (construct-shaped, lands first in the same PR): every board / tree / queue reading a PM seat states — in a claim, a dispatch order, a review, a round report, a seat-post segment — carries the moment it was taken (UTC timestamp, and the ref or tip where the reading is of a tree). A reading without its timestamp is malformed, not current: the reader treats it as untaken. This changes what gets typed, not what has to be recalled, which is the discriminator this card measured.
  • A (the meta-rule, recorded beside B): when a PM-side failure is recorded, the amendment prefers, in order: (a) remove the construct that permits the error; (b) make the correct form the only one the protocol spells; (c) add a check the PM must remember. (c) is what recurred; (a) is what held. Option C (mechanise all four instruments) and D (do nothing) are not taken; the three remaining instruments (fence derivation, census/grep, loop refill) are not mechanised in this ruling — measure B's effect first.

Nothing is re-decided here. C and D stay refused; the other three instruments stay un-mechanised (that non-action is recorded on the card, not in the skill text — the skill carries rules, not history).

What landed — .claude/skills/pm-dispatch/SKILL.md only

One bullet in 「平台读数纪律」 grew by six continuation lines; no existing line was changed, moved or re-wrapped (git diff --stat: 1 file, 6 insertions, 0 deletions).

Where and why there. B is folded into the existing reading-validity bullet — 「零命中必须用确定存在的邻近词反查, 否则零命中不成立」 — because it has exactly the same shape: a condition under which a reading is not a reading. A zero without a control does not hold; a reading without its timestamp is malformed. One rule, stated once, in the file's own register; no new bullet.

Before (origin/main 224f8ea4, L163–L165 — the whole bullet):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。

After (3612bd4e1, L163–L171; L163–L165 byte-identical, L166–L171 new):

- **零命中必须用确定存在的邻近词反查**,
否则零命中不成立;仓不可达同理,⛔ 不因「查不了」
当「查过了干净」—— 在 issue 上贴出给对应座位的现成命令,等读数回贴再派。
**同为成立条件 —— 板面/树/队列读数自带取数时刻**(维护者 2026-09-02 裁「其他同意」):
写进认领、派发令、复核、轮次报告、座位贴段落的读数恒带 UTC 时间戳
(形如 `2026-09-03T00:21Z`),树读数另带 ref 或 tip;无时间戳的读数是格式错误不是现值,
读者按未取处理 —— 改的是键入什么,不是要记住什么。**PM 侧失效的修法按序取**:
(a) 删掉容许出错的构造 → (b) 让正确形态成为协议唯一拼写 → (c) 加一条要记住的检查;
(c) 型屡复发、(a) 型守住 —— 占位符禁令与本条取数时刻皆 (a) 型。
  • B = L166–L169 (first clause). The five reading sites the ruling names, in the file's order of use (认领 · 派发令 · 复核 · 轮次报告 · 座位贴段落); UTC timestamp with one spelled example so the correct form is the only one the protocol spells (the (b)-shaped half, at zero extra lines); ref or tip for tree readings; the malformed-not-current reading rule; the discriminator sentence.
  • A = L169 (second clause)–L171. The (a) → (b) → (c) ranking, the measurement (「(c) 型屡复发、(a) 型守住」), and the two worked (a)-shaped examples the file now carries: the placeholder ban at L150–L153 (unchanged; see finding 1) and this bullet.

Line arithmetic, from the gate's own verdict lines (pnpm check:pm-skill-ratchet):

linesceilingheadroomwidest table row
before, origin/main 224f8ea4✓ … SKILL.md is 989 lines (ceiling 1005; headroom 16).100516642 bytes (pin 642; headroom 0)
after, 3612bd4e1✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).100510642 bytes (pin 642; headroom 0)

Spent: 6 of 16. Paid by deletion: none — I looked for a restatement beside the landing site to delete and found none. Each of the neighbouring bullets' load-bearing phrases is single-sited in this file (维护者中止 ×1 at L173, 聚合读数 ×1 at L170, 共享检出 ×1 at L162, 对向事实 ×1 at L181, pin 反转 ×1 at L182, 邻近词 ×1 at L163); 查重 appears 8× but every other occurrence is a pointer or a different rule (cross-repo shadow check, sub-issue channel), not a restatement of the L176 立单前查重 rule. So the six lines come from headroom, and no #13597-style Phase-2 cut was started. ⛔ No re-wrap as payment: the diff is insertions only. ⛔ No ceiling raise. The first attempt at these lines was 126–133 bytes wide and the ratchet's length rule went red on L166–L169 (4 line(s) over the 120-byte budget); only the six NEW lines were re-wrapped to ≤118 bytes — that is wrapping my own text, not paying with the file's.

Verification facts, as the ruling asked

(1) Did #11901's construct-removal ever reach persistent skill text? — Yes. It landed 2026-08-25, three days before the card.

The triage comment (2026-08-28) concluded 「#11901 的那次「construct-removal」没有落进任何持久指令文本」 from a zero on the single spelling the card's re-check used (绝对路径). That zero is real but measures the wrong spelling. Searched on origin/main 224f8ea4 across .claude/skills/pm-dispatch/** and .claude/agents/os-dev.md (git grep -c -F, summed hits; every zero has a positive control beside it):

spellinghitsreading
绝对路径0the card's own re-check — a true zero, wrong spelling
angle-bracket / angle bracket / placeholder / spell it in words0English spellings of the #11901 triage note never entered the Chinese-register files
尖括号12SKILL.md L150PM 写进 GitHub 的文本 ⛔ 不用尖括号路径占位符, 改写成「后跟显式路径」的说法 … 不产生这个构造才是修法, 回读只是兜底」; os-dev.md L356; platform-readings.md ×10
占位6os-dev.md L357 「尖括号形状片段一律改占位词拼写」; platform-readings.md L248, L253 「运算符用词拼出,或占位词定义一次」; SKILL.md L150; landing-operations.md L79 (unrelated sense)
angle-bracket path-placeholder tokens (the literal word path in angle brackets)9all reader-facing command templates (git show origin/main: + path, git checkout HEAD -- + path …), the same four the triage already classified as not the construct — plus os-dev.md's ablation recipes
positive controlsdomain:44 · 路径68 · Blocked-by:26the paths are readable; the zeros above are measurements

Dating (the clone was shallow — 242 commits, graft at 873ff0f — so git log -S first reported the graft boundary; deepened twice to 2,320 commits, oldest 2026-08-17): git log -S'尖括号路径占位符' -- .claude/skills/pm-dispatch/SKILL.mdde98599, 2026-08-25 09:16Z, PR #12086 「SKILL.md discipline pack — six scoped protocol amendments」 — the PR through which #11901 reached CLOSED/COMPLETED (the card's closedByPullRequestsReferences names exactly that PR). os-dev.md's 占位词 spelling: 04600d9, 2026-08-26 (PR #12440). platform-readings' 用词拼出: same 2026-08-26 commit, widened 2026-08-29 (47fd377).

So the #11901 triage note (os-zhuang, 2026-08-25 05:14Z: 「PM-authored text does not use angle-bracket path placeholders; spell "followed by the explicit path" in words — because it removes the construct instead of adding a check; plus the narrow read-back rule as the backstop」) was delivered in full, four hours later, to SKILL.md L150–L153 — construct-removal as the fix and read-back as the backstop, in those words. The card's discriminator sample stands: the (a)-shaped fix that held is also the one that reached persistent text. The triage's weakening (「若那次修复其实只是一个席位在会话内的习惯 …」) does not apply. Not landed here: the rule already exists; the new text only cites it as the sibling (a)-shaped example (「占位符禁令」).

(2) B's boilerplate cost — light: ≤1 % on claims and dispatch orders, ≈2–5 % on round reports; the round report is the heaviest site.

Reading sites the protocol prescribes, enumerated from SKILL.md at 3612bd4e1:

sitewhere prescribedreadings per artifact (typical)
claim commentL541–L549 template: Serial constraints cleared: (in-flight/board), Container & model: tier from dispatch-gates --tier output (tree)2
dispatch orderL626–L653: 「清单、路径、行号在派发那一刻从树上取」 (tree), same-day churn line (tree), 在飞重叠拦截 (board), stale-premise check 三面 L481–L495 (tree + board)3–6
review / ACCEPTL705–L724: get_files path face, gate-job conclusions, 「抽查读数」2–4
round reportL800–L812: five health indicators (可派发库存 · 决策箱 · finding 数与中位年龄 · 裸卡数与中位年龄 · 发版阻塞) + governed 合并审计 + UNRECOGNISED line5–8
seat-post segmentL285–L300 四段现值 at round boundary; the 开轮标记 (session ID + fire 时刻) and 收班简报 timestamp (L333 anchor) are already timestamped by existing protocol≤4, two already stamped

Per-reading cost: a UTC stamp 2026-09-03T00:21Z is 17 bytes; a tree reading adds a ref/tip such as origin/main 224f8ea4 (≈21 bytes) — ≤ ~40 bytes per reading, plus one date -u per reading pass (most inputs already carry their own time: git --date, updated_at, check-run timestamps).

Measured on this card's own artifacts: the dispatch order for this flight (~5,400 chars) states 2 stampable readings (the origin/main line count — which carried neither stamp nor tip — and the claim-state board reading; the third, the triage's zero, is carried by that comment's own timestamp) → ≈55 bytes, ≈1 %. The claim comment 5518388833 (~2,400 chars) states 1 (the two sibling PRs merged, face free) → 17 bytes, <1 %. A round report with 8 readings pays ≈300 bytes on a multi-KB report, ≈2–5 %. Honest caveat: the byte cost is small; the real cost is that the stamp must be taken at read time — which is precisely the construct the ruling wants, since a reading typed without one is now visibly malformed instead of silently stale.

Gates (every exit captured by redirect before any pipe; each gate's own verdict line quoted)

All at HEAD 3612bd4e1, the final commit; the union was derived AFTER the last edit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no hand-fed paths; stderr: gate list derived from the tree of 'objectstack-ai/objectstack' at commit 3612bd4e1 … change set derived from git — 1 path(s) vs merge base 224f8ea4a). Union = check:doc-formula-expressions (lint pkg) · check:agent-test-spelling · check:doc-authoring · check:pm-governed-merges · check:pm-governed-prose · check:pm-skill-id-lint · check:pm-skill-ratchet · check:skill-frame-sync. Four of the union were not in the dispatch list and were run in addition; four of the dispatch list are not in the union and were run anyway.

gateexitverdict line
check:pm-skill-ratchet (+ --self-test)0✓ check-skill-line-ratchet self-test: 111 cases pass. · ✓ … SKILL.md: widest table row is 642 bytes (pin 642; headroom 0). · ✓ … SKILL.md is 995 lines (ceiling 1005; headroom 10).
check:pm-skill-id-lint0✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).
check:skill-frame-sync0✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
check:skill-frame-freshness0✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).
check:pm-dispatch-gates0self-test suite green (last line ✓ a valueless run record refuses, and its message does not name the OTHER flag)
check:pm-governed-merges0--self-test green (exit 0, no ✗)
check:corpus-claim-drift0exit 0, no ✗
check:nul-bytes0exit 0, no ✗
check:agent-test-spelling (union)0✓ check-agent-test-spelling: 0 violations — 430 file(s) · 5686 bare -- token(s) …
check:doc-authoring (union)0✓ doc authoring guard: 46 published skill files clean — no internal issue-id references. (+3 more ✓ lines)
check:pm-governed-prose (union)0✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others.
check:doc-formula-expressions (union, @objectstack/lint)0 (after building @objectstack/spec, @objectstack/formula, @objectstack/lint under the lock; the first two runs were exit 3 PREREQUISITE NOT MET, recorded as NOT MEASURED, never as a flake)✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 426 files / 1365 TS blocks judged clean by @objectstack/formula. · ✓ … (spec TSDoc): 9 @example(s) judged clean across 1120 packages/spec/src files … · ✓ … (field-level *When): 14 predicate(s) … judged clean; 6 skipped as undeterminable.

Deviation, declared: the ratchet and the @objectstack/formula build ran under scripts/pm/os-verify-lock.sh (slot issue-12911); the batch of seven check:* gates queued under the same lock was killed at the platform's 10-minute foreground cap (exit 143, after 3 of 7 had already returned 0), so the check:* gates were re-run unlocked at HEAD. The lock's own --status text classifies check:* gate scripts as unlocked sibling work it never excludes, and os-dev.md's lock rule names build/test; this is the declared narrowing, not a skipped gate.

Premise checks

  • premise_falsetriage comment 5453234724: 「pm-dispatch: the PM tells every dev to read its comment body back, and never does so itself — 2 of 5 dispatch orders in one round shipped a truncated restore command #11901 的那次「construct-removal」没有落进任何持久指令文本」 — false on its own date; see finding (1). The card's re-check zero on 绝对路径 holds as a zero; it never held as evidence.
  • Held: the dispatch's measurement 「989 lines against ceiling 1005 (headroom 16), widest-table-row pin 642」 — matched by the gate's verdict line before writing.
  • Held: 「the seat has already claimed the card」 — claim comment 5518388833 (branch claude/issue-12911-timestamped-readings, this session); assignee and labels untouched by this flight.
  • Held: skip-changeset applies — nothing is published from any package; the diff is .claude/** only.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…d fix; record the remediation-shape ranking beside it
Every board / tree / queue reading a PM seat states — in a claim, a dispatch
order, a review, a round report, a seat-post segment — carries the UTC moment
it was taken (and the ref or tip when the reading is of a tree). A reading
without its timestamp is malformed, not current: the reader treats it as
untaken. Folded into the reading-validity bullet of 平台读数纪律 beside the
zero-hit control rule, which has the same shape.
Recorded beside it: when a PM-side failure is recorded, the amendment prefers
(a) remove the construct that permits the error, then (b) make the correct
form the only one the protocol spells, then (c) add a check the PM must
remember — (c) recurred, (a) held.
Maintainer ruling 2026-09-02, verbatim: 「其他同意」 (adopting B + A).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 3, 2026 02:33
@os-zhuang
os-zhuang added this pull request to the merge queueSep 3, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33710779173 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 49 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 33711768020 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (1/6) — 失败步骤: Run this shard's tests

    @objectstack/cli:test: FAIL integration test/run-dev-unbuilt-workspace.e2e.test.ts > the mirror direction: a reader that is never coming back > gives up and exits instead of waiting forever
    ↳ 失败原因: @objectstack/cli:test: AssertionError: the harness SIGKILLed the child — it was still alive at the ceiling. cap 180000 ms (RUN_TIMEOUT_MS, constant and load-independent by design); this child ran 1801
    

↳ 失败原因 是判读的关键:超时Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • ⚠️本 PR 过去 24h 已在队列失败 1 次(不含本次)。 内容未变而反复失败 ⇒ 高度怀疑 flaky 测试或与同组 PR 的语义冲突,重排不解决。
  • 过去 24h 队列共有 51 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 3, 2026
@os-project-manager
os-project-manager added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 1596b4cSep 3, 2026
31 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12911-timestamped-readings branch September 3, 2026 09:56
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants

@os-litant@os-zhuang@os-project-manager@claude