Skip to content

agents(os-dev): narrow three test obligations — one-time ablation proofs, import-side tests only on public-surface change, no wording pins - #16460

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-16456-dev-contract-test-obligations
Sep 7, 2026
Merged

agents(os-dev): narrow three test obligations — one-time ablation proofs, import-side tests only on public-surface change, no wording pins#16460
os-zhuang merged 1 commit into
mainfrom
claude/issue-16456-dev-contract-test-obligations

Conversation

@os-steve

@os-steve os-steve commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16456

Governed surface (.claude/**): draft PR, human merge. One file, .claude/agents/os-dev.md, line-neutral at 403/403: every added line is paid by a fold inside the same block, every line is at or under 120 bytes (LC_ALL=C awk, measured max 120, zero over), and the diff stays in the rules-only register (no dates, quotations, issue numbers, narrative or model names). No other file moves — the measurement below says why.

Three test obligations in the dev contract are narrowed (maintainer-directed, verbatim: 「我想的是测试会不会太多,是否都是必要的,是不是应该砍,每次修改都要完整的测试吗」 → 「同意,这几个都处理,你现在就负责派发」). Line numbers: 改前 = origin/main at 6eba38f5a3, 改后 = ddb4213508. In the quoted lines the two angle-bracket placeholders are spelled YOUR_BRANCH and PATH; the file keeps its own spellings — the GitHub sanitizer eats angle-bracket fragments in bodies.

① Reverse verification and ablation are one-time proofs — os-dev.md L210–214 → L210–213

where 改前 改后 paid by
L210 反向验证(回退修复,看诊断变化)先 commit 修复:恢复只是 git checkout YOUR_BRANCH -- PATH 反向验证与消融是一次性证明:前后运行引在 PR 正文与报告里;⛔ 不留永久测试文件。 the block's two commit-first lines (old L210 + old L213) fold into new L211
L211 对着未提交的编辑,git checkout origin/main -- PATH 不留任何恢复点。 两者都先 commit 修复再回退或变异:恢复腿指向 HEAD,HEAD 必须先装着你的实现。 old L210's restore spelling YOUR_BRANCH yields to HEAD, which hard line ① (L217) already mandates
L212 恢复机制与字节一致性证明规则见 AGENTS.md;从已 commit 的状态重跑,红/绿数字才可信。 未提交时 git checkout origin/main -- PATH 不留恢复点,git checkout HEAD -- PATH 删的正是实现。 the block's two uncommitted-hazard lines (old L211 + old L214) fold into this one; 「事后检查全绿」 (consequence narrative) deleted
L213 消融同一条,先 commit 再变异:恢复腿指向 HEAD,HEAD 必须先装着你的实现。 恢复机制与字节一致性证明规则见 AGENTS.md;从已 commit 的状态重跑,红/绿数字才可信。 old L212, unchanged, moved down one
L214 对未提交的实现,一次完美的 git checkout HEAD -- PATH 删的正是实现本身,事后检查全绿。 folded into new L212

No longer owed: a fixture, spec or self-test case added to the tree to make a reverse-verification or ablation proof permanent — the legs stay mandatory (the how-to at L188 and L214–260 is untouched), and the evidence is the quoted before/after run in the PR body and the report.

② Import-side tests only when a public surface changes — os-dev.md L129–130

where 改前 改后 paid by
L129 受影响包 = CI 会测的包,清单读 TURBO_SCM_BASE="$BASE" pnpm exec turbo ls --affected 受影响包 = 本包;import 方只在公开面变化时欠测试:spec 契约、发布的 exports、线上形状。 in place — this line was the definition that made L130 operative (affected = CI's --affected graph, importers included), so it is the echo the ruling names
L130 ⛔ 不按改了哪些包猜:普通 import 被改模块的包也在清单里,欠它们的测试。 公开面字节不变 ⇒ 只欠本包测试与派生门禁,⛔ 不给每个 import 方补测试。 in place

No longer owed: a test in — or a local test run of — every package that merely imports the changed module while every public surface (a packages/spec contract, a published exports entry, a wire shape) stays byte-identical; own-package tests and the derived gates remain owed. When a public surface does change, importers still owe tests, and the consumer-sweep filter at L185 (pnpm --filter '...@objectstack/PKG') remains the enumerator — the turbo ls --affected spelling leaves with the CI-set definition it served.

③ No pin tests on wording — new os-dev.md L227 (the measurement found no prior carrier)

where 改前 改后 paid by
L227 (new) 提示、裁决与错误文案 ⛔ 不 pin,除非消费者解析其原文;断言种类、退出码或具名主体。 the one line freed by ①'s fold (5 → 4)
L228 措辞本身即契约的地方,在 code+status 之上再断言 message 首句,永不取而代之。 unchanged it is the mechanics of the exception (wording that IS the parsed criterion), read directly under the rule

No longer owed: a pin test on a remedy string, a verdict line, a prompt or an error message's prose; the assertion is on the kind, the exit code or the named subject, and wording is pinned only where a consumer parses it (L228 keeps the code+status-first mechanics for that case).

Measurement — every carrier of the three obligations, by file and line

First pass grep -n -E '欠|import|反向验证|消融|pin|文案|原文' over the five named files, second pass a read of each hit's neighbourhood, third pass a wider grep (受影响|affected|下游|消费者|清扫|判定行|message|wording|fixture|snapshot|措辞|永久) over .claude/agents/os-dev.md, all of .claude/skills/pm-dispatch/ (references and lanes included), AGENTS.md, .claude/skills/dogfood-verification/SKILL.md and the published skills/objectstack-pm-dispatch/.

Moves (all in os-dev.md): L129–130 (②), L210–214 (① — the block gains the one-time-proof rule and folds 5 → 4), new L227 (③).

Read true under the ruling, left in place:

  • os-dev.md L185–187 — consumer-sweep direction on a contract tightening: the public-surface case of ②.
  • os-dev.md L188 — cross-package type reverse verification: already one-time (「确认转红,再恢复」).
  • os-dev.md L225–226, L228 — rejection tests assert code+status; wording only where it is itself contract: ③'s direction.
  • os-dev.md L236–237 — fixture sweep by a narrowed rule's consumer radius: a rule narrowing is a public-surface change (②).
  • os-dev.md L265–271 — Definition of done test clause and the two packages/cli tier sentences (L131, L266): 「受影响包」 now reads through L129; both read true unchanged.
  • os-dev.md L369 — report tests field (「ablation: rebuild + on-disk mutation proof」): the quoted-run evidence of ①.
  • references/core-rules.md — no carrier (L61 pin is a version pin; L150 is the report contract).
  • references/review-checklist.md L31 (real command + output as test evidence), L53–58 (rejection assertions on code/status), L59–62 (N-package sweep evidence only on a contract tightening) — consistent with ①, ③, ②; no wording-pin demand anywhere in the file.
  • references/contract-review.md — no carrier.
  • SKILL.md L182–183 (behaviour pins reversed on a contrary fact — behaviour, not wording), L546 (repo-wide pin sweep on a public-semantics flip: the public-surface case), L548 (code+status minimum), L596 — consistent; no line moves.
  • references/lanes/services.md L17–18 — 「每条否定性 pin 都要消融验证:被禁行为放回、测试转红、报失败输出、恢复」: already the one-time form of ① (report the failing output, restore).
  • references/lanes/spec.md L38–39 — contract-surface cards run consumer-package tests: the public-surface case of ②.
  • references/dispatch-runbook.md L223–225 — the pin sweep on a semantics flip; the grep of error messages is the search instrument for existing pins, and L224 asserts substance, not prose.
  • AGENTS.md L364–371 — commit-first for reverse verification; consistent with ①.
  • published skills/objectstack-pm-dispatch/rules/dev-template.md L71–72 (generic 「run the affected packages'」 with no importer definition) and L95–96 (wording only where it is itself contract) — no echo; skills/** untouched, so the two-readings clause does not apply.

The wording-pin obligation (③) had no carrier in the corpus — it was a habit; rule ③ is added as a fold, next to its nearest neighbour L228.

Line pins and consistency

  • check-skill-line-ratchet live: ✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0). — no ceiling touched; self-test 155 cases pass.
  • Every line ≤ 120 bytes: LC_ALL=C awk over the file → max=120 over120=0 (the file already carried 120-byte lines).
  • On-disk proof of the fold (not the editor's exit code): grep -c of six removed anchors → 0 each; six inserted anchors → 1 each.
  • git merge-tree --write-tree HEAD origin/main → exit 0, 0 CONFLICT lines against origin/main at 245c6a23cd (tree 4c4791bb1c).

Gates (union run at ddb4213508, the final commit; every exit captured before any pipe)

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path passed; change set from the merge base 6eba38f5a: 1 path). Reconciled: ✓ dispatch-gates --ran: 16 derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED. (exit 0).

  • pnpm check:pm-skill-ratchet exit 0 — the two lines quoted above; declared cross-file moves: 1, total ceilings down 9 lines (main's state, untouched).
  • pnpm check:pm-skill-id-lint exit 0 — ✓ check-skill-id-lint: 26 file(s) clean (pattern /#[0-9]{3,}/g).
  • pnpm check:skill-frame-sync exit 0 — 4 axes … binding sentence present in all 2; 4 count mention(s) agree; 75 markdown files scanned for undeclared copies.
  • node scripts/pm/check-clause2-carriers.mjs --self-test exit 0 — ✓ check-clause2-carriers self-test: 190 cases pass.
  • node scripts/pm/check-governed-merges.mjs --test .claude/agents/os-dev.mdexit 3One hit governs the whole PR — 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question. .claude/** ×1.
  • pnpm check:pm-governed-merges exit 0 — self-test 274 assertions.
  • pnpm check:nul-bytes exit 0 — scanned 8078 text file(s) … no raw ASCII control bytes.
  • pnpm check:agent-model-declared exit 0 — 0 justified inherit(s); no definition leaves its tier to the dispatching session.
  • pnpm check:agent-test-spelling, check:doc-authoring, check:driver-memory-census, check:refd-timer-probe, check:watch-hint-literal, check-closing-keyword-parity (+ self-test), check-comment-mask-corpus, check-governed-queue-guard --self-test — all exit 0.
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions — first run exit 3 PREREQUISITE NOT MET — the workspace package @objectstack/formula is not built (nothing measured); prerequisites built under the verify-lock, re-run: exit 0 — ✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 430 files / 1371 TS blocks judged clean by @objectstack/formula.
  • Whole-repo pnpm lint through bash scripts/pm/os-verify-lock.sh (slot issue-16456, one acquisition, full 540s budget): os-verify-lock: VERDICT command-exit 0 · held the lock 89s (1m29s) · waited 0s (shared-box seconds).

No ablation applies — the diff is instruction text; the proof is the gates above.

维护者速读(草稿)

  • 改了什么:dev 契约(.claude/agents/os-dev.md)里三条测试义务收窄:反向验证与消融只是一次性证明、引在 PR 与报告里,不再留永久测试文件;只有公开面(spec 契约、发布的 exports、线上形状)变化时,import 方的包才欠测试;提示、裁决与错误文案不做 pin 测试,除非消费者解析其原文。403 行上限不动,只折不加。
  • 为什么改:您的判断「测试会不会太多」有实测支撑——3,388 个测试文件对 2,183 个源文件,每天新增 35–89 个,而这三条义务正是契约自己在要求这个增速。
  • 风险与代价(含回滚):风险是 import 方的回归少了一道本地预检,但 CI 仍跑全套、公开面一变 import 方照旧欠测试;回滚只需 revert 这一个 commit。
  • 席位意见:
  • 你要做的:确认三条新规则的措辞是您的本意(尤其②「公开面」的三项定义),然后人工合并。

🤖 Generated with Claude Code

https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox


Generated by Claude Code

…ofs, import-side tests only on public-surface change, no wording pins

Rules-only folds under the unchanged 403-line ceiling, every line at or under
120 bytes:

- L129-130: the affected set is the changed package; importers owe tests only
  when a public surface changes (spec contract, published `exports`, wire
  shape); byte-identical public surface owes own-package tests and the derived
  gates only.
- L210-213: reverse verification and ablation are one-time proofs quoted in
  the PR body and the report, never a permanent test file; the two
  commit-first lines and their two uncommitted-hazard lines fold into one each.
- L227: prompts, verdicts and error copy are not pinned unless a consumer
  parses the text; assert the kind, the exit code or the named subject.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
@github-actions github-actions Bot added the size/s label Sep 7, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 7, 2026
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 7, 2026
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Verdict: ACCEPT (governed — to the approvers)

Implemented-by: os-dev flight Y (fable), branch claude/issue-16456-dev-contract-test-obligations, head ddb4213508
Reviewed-by: pm-dispatch skills seat (contract-tier read in a detached compare worktree at ddb4213508, merge-base 6eba38f5a3)

What was checked

  • One file, .claude/agents/os-dev.md, +6/−6, 403/403 lines with every line at or under 120 bytes; rules only, no issue numbers or provenance in the changed lines.
  • ② (import-side tests): lines 129–130 now read 「受影响包 = 本包;import 方只在公开面变化时欠测试:spec 契约、发布的 exports、线上形状。」 and 「公开面字节不变 ⇒ 只欠本包测试与派生门禁,⛔ 不给每个 import 方补测试。」 — the ruling's substance; the consumer sweep at line 185 stays the enumerator for the case where a public surface does change.
  • ① (ablation as a one-time proof): the commit-first block folds 5 → 4 lines and opens with 「反向验证与消融是一次性证明:前后运行引在 PR 正文与报告里;⛔ 不留永久测试文件。」; the recovery mechanics (commit first, HEAD restore leg, the uncommitted-edit trap) survive in the three lines below it.
  • ③ (no wording pins): new line 227 「提示、裁决与错误文案 ⛔ 不 pin,除非消费者解析其原文;断言种类、退出码或具名主体。」, placed beside the existing rule that a message's first sentence is asserted on top of code+status only where the wording is the contract — the two read as one rule. The measurement found no prior carrier of this obligation in the corpus (a habit, not a line), so it is an addition paid by the fold above.
  • Carriers outside the five named files (lanes/services.md, lanes/spec.md, dispatch-runbook.md, AGENTS.md, the published dev-template) already read true under the ruling and are listed with line numbers in the PR body; the Definition of done's test clause and both cli tier sentences read true unchanged.
  • Gates in the compare worktree: ratchet live green at 403/403 (self-test 155), check-governed-merges --test exit 3 (governed), check:skill-frame-sync and check-clause2-carriers green; merge-tree --write-tree against origin/main clean. CI on the head: 17 success / 12 skipped / 0 failure.

Rulings

  • No open question. The dev's three named deviations are accepted as stated: the harness-written co-author trailer on the commit (the AGENTS.md exemption), the 速读 draft in os-dev.md's five-heading form, and keeping the consumer-sweep filter as the enumerator.

Landing

Governed surface: reviewers os-zhuang and hotlong requested, needs-user-decision added, the final 维护者速读 follows this comment. Human merge; the seat never flips ready or queues a governed PR. On merge the seat strips pm:dispatched and clears the assignee on #16456, then dispatches #16388 (queued behind this file).


Generated by Claude Code

@claude
claude Bot requested review from hotlong and os-zhuang September 7, 2026 03:09
@claude

claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

维护者速读(skills 席,2026-09-07T03:1xZ)

事情:按您 02:0xZ 的指示(「同意,这几个都处理,你现在就负责派发」的 C 项),dev 契约 .claude/agents/os-dev.md 收窄三条测试义务,一个文件、+6/−6、403 行钉子不动:① 反向验证与消融只作一次性证明(前后运行引在 PR 正文与报告),⛔ 不再留永久测试文件;② import 方的包只在公开面(spec 契约、发布的 exports、线上形状)变化时欠测试,公开面不变只欠本包测试与派生门禁;③ 提示/裁决/错误文案不做 pin,除非消费者解析其原文。

为什么:测试文件 3,388 对源码 2,183,每天新增 35–89 个,增长是契约义务的函数;抽样 145 个新测试里 import 方补测的只有 6 个,所以 ② 的直接效果小,① 与 ③ 是主要来源。三条都不动「怎么做验证」,只动「留不留下来」。

风险:低。契约收窄意味着某些跨包回归改由合并队列的 CI 捕获(受影响集 + 跨包扫描),而不是由 dev 提前补测;公开面变化时的 import 方义务仍在。文本层面 ratchet / frame-sync / clause-② 载体检查全绿。

要您做什么:一个决定——合并或退回。审批人 os-zhuang / hotlong 已被请求复审。

席位意见:建议合并。三条与您对测试总量的判断一致;若想保守,可只合 ① 与 ③、把 ② 退回(我们会拆成单行 PR)。


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation needs-user-decision size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants