docs(spec): anchor the BatchOptions.validateOnly tombstone to the release that removed it - #13127
Conversation
…elease that removed it
The tombstone read "... was removed from BatchOptions in @objectstack/spec." —
a removal citing nothing durable. Of the 132 audited tombstone prescriptions it
was the only one left with no reference at all: no ADR id, no `os migrate`
command, and — uniquely — no version anchor either. `retiredKey`'s own contract
asks for one ("State what replaced the key, the version that removed it, and
the one-line fix"), and 128 siblings carry it.
The version is READ, never recalled. Three independent sources agree on 17.0.0:
* packages/spec/CHANGELOG.md — "ec796d5: feat(spec)!: retire
`BatchOptions.validateOnly` ..." sits under the `## 17.0.0` Major Changes
heading. The nearest-heading method was validated first against two
positive controls in the same population (`global_nav` from
`ACTION_LOCATIONS`; `IDataDriver.findStream`), both of which resolve to
17.0.0 and match the anchors already written in their own tombstones.
* git log --follow -- packages/spec/src/api/batch.zod.ts — commit ec796d5;
the earliest GA tag containing it is @objectstack/spec@17.0.0, and no
pre-17 spec tag carries it.
* packages/spec/spec-changes.json — batch-options-validate-only-retired,
toMajor: 17.
Anchor spelling byte-matches the sibling convention in the same api/ family
(analytics.zod.ts: "`query` was removed from AnalyticsQueryRequest in
@objectstack/spec 17.0.0.").
Guidance text only: the key stays retired, stays typed `never`, and is still
rejected with the same prescription. The generated API reference pages under
content/docs/references/ carry the prescription verbatim and were regenerated
by tooling (`gen:docs`), moving exactly the 7 lines that spell it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 74ae54b9ab94cf88a6f9ebdb928bc60853cfe05f && git checkout 74ae54b9ab94cf88a6f9ebdb928bc60853cfe05f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin adf70f763bce48915cf4eb90b66de5b0dc5249f8 93110493089672bcf09077cbf4411a2559efc055 && git checkout -B drift-repro adf70f763bce48915cf4eb90b66de5b0dc5249f8 && git merge --no-ff 93110493089672bcf09077cbf4411a2559efc055
node scripts/docs-audit/affected-docs.mjs --json adf70f763bce48915cf4eb90b66de5b0dc5249f8 |
Fixes #13020
Executes the maintainer ruling on that card (comment 5459668566, verbatim 「同意」 — option B, machine-sourced): the
options.validateOnlytombstone gains a version anchor, with the removal version read mechanically frompackages/spec/CHANGELOG.mdrather than recalled.The anchor
packages/spec/src/api/batch.zod.ts, one line inside theretiredKey(...)prescription:`options.validateOnly` was removed from BatchOptions in @objectstack/spec.`options.validateOnly` was removed from BatchOptions in @objectstack/spec 17.0.0.Nothing else in the string moves. The key stays retired, stays typed
never, and is still rejected with the same prescription — this is guidance text gaining a citation, not an accept-set change.Why it was owed
Of the 132 audited tombstone prescriptions, this was the only one the #12522 strip left citing nothing durable: no ADR id, no
os migratecommand, and — uniquely — no version anchor either.retiredKey's own contract asks for one:128 siblings carry it. This one now does too.
The three-source reading
The ruling required the version be read, with a positive control in the same population. All three sources agree on 17.0.0:
packages/spec/CHANGELOG.md## versionheadingec796d5: feat(spec)!: retire BatchOptions.validateOnly — a dry-run flag that was never implemented (#4052)at line 9927, under## 17.0.0→ Major Changes. The same entry also appears at line 57120 under## 17.0.0-rc.1— the pre-release that first carried it.git log --follow -- packages/spec/src/api/batch.zod.tsec796d5fd(feat(spec)!: retire BatchOptions.validateOnly — a dry-run flag never implemented (#4052), 2026-07-30).git tag --contains ec796d5fdnames 7 spec tags; the earliest GA one is@objectstack/spec@17.0.0, and no pre-17 spec tag carries it.packages/spec/spec-changes.jsonsurface: api.batchOptions.validateOnly·migrationId: batch-options-validate-only-retired·toMajor: 17Agreement statement: the changelog GA heading, the earliest GA tag containing the retiring commit, and the ledger's
toMajorall name the 17 line, and the two that carry a full triple name17.0.0. There is no disagreement to adjudicate, so the question does not return to the card.Positive controls
The nearest-heading method was validated on two sibling retirements in the same population before the target reading was trusted — each one already carries a version anchor in its own source tombstone, so the method's answer is checkable against an independent fact:
global_navfromACTION_LOCATIONS(e027b3e)## 17.0.0ui/action.zod.ts:... in @objectstack/spec 17 (#6888, ADR-0049 ...)✔IDataDriver.findStream(c6d1cb4)## 17.0.0data/driver.zod.ts:... was removed in 17.0.0 (#4484, ADR-0049 ...)✔Both controls also reproduce the target's exact two-hit shape — one hit under a
17.0.0-rc.Nheading, one under GA17.0.0— so the target's duplicate hit is the population's normal shape, not an ambiguity.Sibling-form citation
The anchor spelling byte-matches the convention in the same
api/family —packages/spec/src/api/analytics.zod.tsline 52:Same unbackticked type name, same
in @objectstack/spec VERSION.tail, same trailing period-plus-space before the continuation. Acrosspackages/spec/srcthe anchored spellings count 87 ×@objectstack/spec 17.0.0and 70 ×@objectstack/spec 17; the fuller triple is both the majority form and the one this file's nearest sibling uses.Consequentials
content/docs/references/**is a generator-owned artifact (scripts/regen-artifacts.mjs), and the API reference pages carry the prescription verbatim. Regenerated by tooling —pnpm --filter @objectstack/spec gen:docs— never by hand:content/docs/references/api/batch.mdx— 5 linescontent/docs/references/api/protocol.mdx— 2 linesMeasured: reducing every changed line in those two files to its delta yields exactly
7 ×removedin @objectstack/spec. It was neverand7 ×addedin @objectstack/spec 17.0.0. It was never, and the count of changed lines not containingvalidateOnlyis 0. The regeneration moved the anchor and nothing else.One
patchchangeset:.changeset/batch-validateonly-tombstone-version-anchor.md.Verification
Gate family derived mechanically at the final commit —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which reads its own change set from the merge base (4 paths, three-dot semantics) rather than a hand-built list. 52 families matched; all were run.Union re-run on the final commit
931104930, with each gate's own verdict line:check:generated✓ All 14 generated artifacts are up to date.check:docs✅ 231 generated files in sync with packages/speccheck:authorable-surface✓ check:authorable-surface authorable-surface/ + authorable-defaults/ (+ its .base.json anchor) + JSON schemascheck:nul-bytescheck-nul-bytes: OK (scanned 7286 text file(s) ... no raw ASCII control bytes).check:strictness-ledger,check:liveness,check:merge-driverTests and typecheck:
pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/batch.test.ts src/api/validate-data.test.ts src/shared/retired-key-migrate-sentence.test.ts→Test Files 3 passed (3) · Tests 52 passed (52)pnpm --filter @objectstack/spec typecheck→ exit 0;check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.jsontsc --noEmit --listFilesputs the edited file in the program —packages/spec/src/api/batch.zod.ts, 1 occurrence out of 1002 program files.Pin triage. No test asserts the literal guidance string: grepping the old sentence across all
*.test.tsreturns nothing. The two suites that exercise the tombstone match on regexes that are indifferent to the anchor —toMatch(/validateOnly.*removed|never implemented/i)andtoMatch(/validateOnly/)— so neither a pin update nor a pin replacement is owed here.retired-key-migrate-sentence.test.tspins theos migratesentence class-wide and is untouched by this edit; it was run anyway and is green.No ablation is owed. This is a one-line prose anchor inside a
describe/error string. There is no guard to delete and no behaviour whose absence a mutation could demonstrate — the rejection it participates in is already covered by the two suites above.Not measured — stated as such, not as green
Three derived families exited non-zero for a reason that is not a finding, each self-declaring it:
check:dual-build-cjs-loads—PREREQUISITE NOT MET ... ⛔ This is NOT a pass: nothing was measured.It needs a full workspace build (34 packages lackdist/). Repo-wide build gate; CI owns that run.scripts/check-dev-prereqs.mjs—The workspace is not built — 1 unmet precondition, not a list of problems.Same cause.scripts/check-test-completeness.mjs— a usage error when invoked bare: it consumes a turbo test log CI produces.A fourth,
scripts/pm/check-half-states.mjs, exits 3 withPREREQUISITE NOT MET — the token in the environment is not a valid GitHub credential ... It is not a clean board and it is not a dirty one — it is no reading at all.The form CI actually runs is the--self-testaliaspnpm check:pm-half-states, which is green.Three further gates first failed as
PREREQUISITE NOT MET — package not built(check:skill-examples,check:doc-formula-expressions,check:doc-security-posture). Their prerequisites were built and all three then passed — they are counted green above, not waived.Generated by Claude Code