Skip to content

docs(spec): fix AgentSchema's @example and four stale .strict() tombstone rationales - #10922

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-10731-agent-tombstone-prose
Aug 21, 2026
Merged

docs(spec): fix AgentSchema's @example and four stale .strict() tombstone rationales#10922
os-elon merged 1 commit into
mainfrom
claude/issue-10731-agent-tombstone-prose

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10731

Two contained prose repairs in packages/spec/src/ai/agent.zod.ts, plus the triage-approved
sweep for the same stale phrasing across packages/spec/src/. Prose only — every changed
line is a comment; no schema shape, acceptance behaviour or .describe() semantic moves.

1. AgentSchema's @example taught a key the same schema rejects

The docblock example wrote knowledge: { sources: […], indexes: […] }, while the same file
declares knowledge as retiredKey() (i.e. z.never()) about 110 lines below. The line is
dropped; the example keeps skills, which is the block's whole point.

Verified as an A/B against the built dist a consumer actually resolves — not asserted.
Both snippets were type-checked against packages/spec/dist/index.d.ts:

=== as-written (the example as it stood at :139): tsc EXIT=2 ===
as-written.ts(8,3): error TS2322: Type '{ sources: string[]; indexes: string[]; }'
is not assignable to type 'undefined'.
=== as-fixed (the example after this change): tsc EXIT=0 ===

Line 8 col 3 is exactly the knowledge: line. The card's premise holds: the canonical example
an upgrading author copies did not compile.

Note on gate coverage, since it is easy to over-claim: check:skill-examples compiles only
marked fences under skills/ and content/docs, and this @example is published to neither
(support_tier_1 appears nowhere in content/docs). Nothing in CI was compiling it — the A/B
above is the measurement, and it is why the defect survived.

2. The stale .strict() rationale — and the sweep

Four tombstone rationales still argued "the schema is not.strict(), so a plain deletion
would silently strip the key". The #4001strictObject conversion made that false for the
schemas named. Each now rests on what is load-bearing today: the prescription is the
payload
— an unknown-key rejection carries neither the FROM → TO mapping nor the migration
command — plus the never typing that still fails tsc at the authoring site. Every
tombstone stays
; only the stated reason changes.

Fixed (owning schema confirmed strict at the cited line):

FileSiteOwner is strict because
ai/agent.zod.tstools tombstone :226AgentSchema = strictObject (:143)
data/field.zod.tsconditionalRequired tombstone :1209FieldSchema = strictObject (:663)
ui/action.zod.tsexecute tombstone :976ActionSchemaactionObject() = strictObject (:765)
shared/retired-key.tsmodule docblock :7premise named FieldSchema, now strict
ai/agent.test.tscomment :71mirrored the agent.zod.ts claim verbatim

The card's assumption that the conversion was repo-wide is itself imprecise, and that
mattered
— most sites carrying this phrasing are still accurate and were deliberately left
alone. Each was checked against its owning declaration rather than pattern-matched:

  • shared/retry-policy.zod.ts:116RetryPolicySchema is a plain z.object (:176). Accurate.
  • shared/mapping.zod.ts:88FieldMappingSchema is a plain z.object (:74). Accurate.
  • kernel/manifest.zod.ts:512ManifestSchema is a plain z.object (:132). Accurate.
  • kernel/metadata-plugin.zod.ts:495MetadataPluginConfigSchema plain z.object (:461). Accurate.
  • api/rest-server.zod.ts:120, :511RestApiConfigSchema (:48) / RestServerConfigSchema (:481) both plain. Accurate.
  • integration/connector.zod.ts:798ConnectorSchema plain z.object (:690). Accurate.
  • conversions/registry.ts:3114BookGroupSchema plain z.object (system/book.zod.ts:82). Accurate.
  • migrations/registry.ts:3413HookContextSchema plain z.object (data/hook.zod.ts:334). Accurate.
  • data/field.zod.ts:148 — already past tense ("was not .strict()"). Accurate.

FENCE honoured:ui/component.zod.ts is held by in-flight #10054 and was not edited. It
also turns out to carry no strictness claim at all (grep for "not .strict()" there
returns nothing), so there was no hit to file either.

One bounded in-place fix, declared

data/field.zod.ts:1180 said "This object is NOT .strict()" in the present tense about
FieldSchema — false since #4001, and inconsistent with :148 in the same file, which already
uses the past tense for the same episode. Corrected to past tense with the closing noted. This
is outside the rider's literal wording (it is a removal-history comment, not a retiredKey
docblock), so it is called out here rather than folded in silently: same defect class, same
file already open, mechanical, evidence-pinned, no new verification surface.

Clause-② proof (mechanical)

Every changed line classified against ^(//|\*|/\*\*|/\*|\*/) after trimming:

packages/spec/src/ai/agent.test.ts: +4 -3 all comment-classified: true
packages/spec/src/ai/agent.zod.ts: +9 -5 all comment-classified: true
packages/spec/src/data/field.zod.ts: +9 -6 all comment-classified: true
packages/spec/src/shared/retired-key.ts: +15 -7 all comment-classified: true
packages/spec/src/ui/action.zod.ts: +6 -3 all comment-classified: true
TOTAL changed lines: 67
NON-COMMENT (code) lines: 0
VERDICT: PASS — every changed line is comment prose; no code line touched.

Verification — gate union pinned to b3d1970d87

Gates were re-derived from the actual diff with node scripts/pm/dispatch-gates.mjs (no paths
passed — it takes its own change set from the merge base), which named families beyond the
dispatch list, including the test-file convention gates. All of the following ran against
b3d1970d87 with a clean tree, quoting each gate's own verdict line:

  • pnpm --filter @objectstack/spec test415 files / 11049 tests passed
  • pnpm --filter @objectstack/spec typecheck → EXIT 0
  • pnpm --filter @objectstack/spec check:generated"✓ All 14 generated artifacts are up to
    date."
    — this prose feeds no generated artifact, so nothing was regenerated
  • check:strictness-ledger → "✓ strictness ledger: 60 file(s) … no undeclared schema files"
  • check:doc-formula-expressions → "✓ 9 @example(s) judged clean across 977 packages/spec/src files"
  • Green with exit 0: check:cross-package-test-inputs, check:empty-state, check:liveness,
    check:merge-driver, check:slot-lookup, check:spec-parsed-alias, check:test-source-alias,
    check:type-source-resolution, check:variant-docs, check-ci-filter-parity,
    check-plugin-teardown-shape, check-affected-docs, check:query-options-erasure,
    check:engine-double-contract, check:where-matcher, check:type-check-coverage,
    check:changeset-gate-self-tests, check:objectui-changeset, check-adr-0087-registration,
    check-changeset-no-major, check-empty-changeset, check:nul-bytes

Declared narrowings (CI runs the full farm regardless):

  • pnpm lint repo-wide was narrowed to the changed files, and the narrowing is measured rather
    than assumed: population 4787 files, read from ESLint's own isPathIgnored() config
    resolution (0 ignored) — not a guess about what counts; 5 files linted, count read from
    --format json output length, 0 errors / 0 warnings; and config invariance — this repo
    runs one eslint.config.mjs which, in its own words at :327, "never enables type-aware
    linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" — so a
    comment-only diff in 5 files cannot move the verdict on any of the 4782 untouched files.
  • check:type-check-debt --re-measure and check-dev-prereqs were not measured locally:
    both require the full workspace closure built, and this fresh worktree has only
    @objectstack/spec and @objectstack/formula built. check-dev-prereqs reports the
    precondition explicitly — "The workspace is not built — 66 of 67 workspace packages declare an
    entry point under dist/ that is not on disk" — which is an unmet precondition, not a finding
    against this diff. A comment-only diff cannot move a tsc error count.

Changeset

.changeset/olive-pumas-repeat.md, @objectstack/specpatch. The AgentSchema docblock
ships in the published .d.ts, so the corrected example is consumer-visible text. Non-breaking,
so no ADR-0087 marker is required — check-adr-0087-registration agrees: "this PR adds no
declared-breaking changeset".

Generated by Claude Code


Generated by Claude Code

…bstone rationales
`AgentSchema`'s own `@example` wrote `knowledge: { sources, indexes }` — a key the
same file declares as `retiredKey()` about 110 lines below. The canonical example an
upgrading author copies taught a key the schema rejects, and typed `never` fails
`tsc` at the authoring site. Drop the line; the example keeps `skills`, which is the
whole point of the block.
Four tombstone rationales still argued from "the schema is not `.strict()`, so a
plain deletion would silently strip the key". The #4001 `strictObject` conversion
made that false for `AgentSchema`, `FieldSchema` and `ActionSchema`, and for the
premise in `shared/retired-key.ts`'s own module docblock. Each now rests on the
reason that is load-bearing today: the prescription is the payload, because an
unknown-key rejection carries neither the FROM -> TO mapping nor the migration
command, and the key is typed `never` so the mistake still fails `tsc` first.
Every tombstone stays; only the stated reason changes.
Prose only — no schema shape, acceptance behaviour or `.describe()` semantic moves.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 4 documentable anchor(s).

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via AgentSchema (symbol))
  • content/docs/concepts/metadata-driven.mdx(via FieldSchema (symbol))
  • content/docs/data-modeling/external-datasources.mdx(via FieldSchema (symbol))
  • content/docs/data-modeling/field-types.mdx(via FieldSchema (symbol))
  • content/docs/data-modeling/validation-rules.mdx(via FieldSchema (symbol))
  • content/docs/deployment/troubleshooting.mdx(via FieldSchema (symbol))
  • content/docs/deployment/validating-metadata.mdx(via FieldSchema (symbol))
  • content/docs/getting-started/quick-reference.mdx(via AgentSchema (symbol), FieldSchema (symbol))
  • content/docs/kernel/contracts/data-engine.mdx(via FieldSchema (symbol))
  • content/docs/protocol/backward-compatibility.mdx(via FieldSchema (symbol))
  • content/docs/protocol/objectql/types.mdx(via FieldSchema (symbol))

2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v13.mdx(via AgentSchema (symbol))
  • content/docs/releases/v17.mdx(via FieldSchema (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/shared/retired-key.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 58563be069a70d09c4d9cc5f522bb27c5f8a78b2packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1f890e73ff5a088a362d4b71234b2ae65f49c377 — the merge of head b3d1970d87704b475e950d95b1f55406e4b49eff into base 58563be069a70d09c4d9cc5f522bb27c5f8a78b2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1f890e73ff5a088a362d4b71234b2ae65f49c377 && git checkout 1f890e73ff5a088a362d4b71234b2ae65f49c377
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 b3d1970d87704b475e950d95b1f55406e4b49eff && git checkout -B drift-repro 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 && git merge --no-ff b3d1970d87704b475e950d95b1f55406e4b49eff
node scripts/docs-audit/affected-docs.mjs --json 58563be069a70d09c4d9cc5f522bb27c5f8a78b2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 58563be069a70d09c4d9cc5f522bb27c5f8a78b2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@os-elonClaude

Copy link
Copy Markdown
CollaboratorAuthor

Review: ACCEPT (reviewer of record: PM seat domain:spec, session_01B4h3medzvhB9rpfoja9jcw)

Verified against GitHub, not the report:

  • Form: draft → main, first line Fixes #10731 — correct closure shape (full card: both premises repaired + triage-approved sweep, nothing in a decision box).
  • Prose-only confirmed from the diff itself: all five source hunks are comment/docblock lines; the changeset accounts for the remaining 21 added lines. The three strictness claims the rewrite rests on are visible in the diff's own context lines (AgentSchemastrictObject :223, FieldSchemastrictObject :1177, actionObject()strictObject :976) — the new rationales are anchored, not asserted.
  • Fence: ui/component.zod.ts absent from the changed-file list (held serial for the in-flight retirement); no strictness claim there to file, as the body records.
  • Adjacent in-place fix (data/field.zod.ts:1180 tense correction): four conditions pass — same defect class (stale strictness prose), mechanical, file already open in this diff with no other claim, same gate family — and it is declared in the body with the :148 consistency evidence. Not REWORK material.
  • Head pinning: report/body gate union pinned to b3d1970d87 = PR head sha exactly. Declared narrowings (eslint population-measured; type-check-debt/dev-prereqs deferred to CI with the precondition quoted) are honest and acceptable for a 0-code-line diff.
  • Changeset: @objectstack/spec is published; patch changeset present and correctly chosen over skip-changeset — the corrected @example ships in the published .d.ts.

Contract-review gate (clause-②): path leg fires (packages/spec/src/**), dispatch tier opus < CONTRACT_REVIEW_TIER. Cleared by fable-tier seat self-review per the maintainer ruling recorded at #10822: the legal-metadata-set check is exactly the mechanical clause-② proof in the body — 67/67 changed lines comment-classified, 0 code lines, no .describe() or shape edit — so accept/reject behaviour is byte-identical by construction. Gate satisfied.

Sweep out-of-scope outputs (grouped, for triage) — sweep criterion: sites carrying the "not .strict()" phrasing, each checked against its owning declaration:

Landing: waiting on CI convergence on b3d1970d87 (Lint & Repo Gates + TypeScript Type Check conclusions) → then flip ready + auto-merge. On merge: drop pm:dispatched from the card, verify closed_by_pull_requests closes it and nothing else.


Generated by Claude Code

@os-elon
os-elon marked this pull request as ready for review August 21, 2026 17:40
@os-elon
os-elon enabled auto-merge August 21, 2026 17:40
@os-elon
os-elon added this pull request to the merge queueAug 21, 2026
Merged via the queue into main with commit 5acb58dAug 21, 2026
33 checks passed
@os-elon
os-elon deleted the claude/issue-10731-agent-tombstone-prose branch August 21, 2026 17:59
@github-actions

Copy link
Copy Markdown
Contributor

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

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

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

  • Console Pin Gate — 失败步骤: Build the Console SPA at the pinned objectui SHA

    ✗ Build failed in 5.80s
    

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

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

  • ⚠️本次没有可用的聚合签名(日志里没有能解析出测试文件名的 FAIL 行)—— 这不是「没有同签名的其他 PR」,是这一轮没测到。跨 PR 聚合本次不可用,请手工比对其他 PR 的同类评论。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

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

分诊清单:

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

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

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spec] AgentSchema's own @example teaches the retired agent.knowledge key, and the tools tombstone cites a stale .strict() rationale

2 participants

@os-elon@claude