Skip to content

fix(spec): author-visible refusals and prescriptions state each decision in words instead of a tracker number (stage 3) - #21521

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20749-spec-strings-stage3
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-20749-spec-strings-stage3

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Part of #20749
Clause-②: no

Stage 3 of the domain:spec lane's share under the maintainer's A / A ruling (5902360492): packages/spec's runtime strings, which sit outside the prose-id ledger. A fresh census first, then the first group, the author-visible refusals and prescriptions, in form D. 13 messages with 14 tracker-number occurrences in five packages/spec/src/data sources. The card stays open for the later stages, so this PR carries no closing keyword. Text only: no key, schema shape, condition, code path, error code or status moves (AST skeleton proof below, 6 of 6 SAME).

The census (at 85e29b8858)

packages/spec/src is outside scripts/doc-authoring-prose-id.baseline.json, so the census is this lane's instrument there. It re-implements the #20513 dev round's instrument from its stated semantics (dev report 5900801368): a TypeScript-AST walk over every non-test source of packages/spec/src (test files counted apart) that folds a message before matching (a maximal chain of string pieces joined by +, a template literal, parentheses, a string array joined with .join(sep), and strings nested in a span, in a call or in a conditional that is an operand of the chain, all folded into the outermost message). The match is the gate's own id pattern. Hit lines come from the string leaf that carries the id, never from the raw span, so comments are never read.

  • Lit controls: authoring-key-lint.ts:104 (single line) reads 1 message; driver.zod.ts:299-301 (a three-line + chain, the id on its third line) reads ONE message; api/error-code-ledger.zod.ts:1773-1776 reads ONE message with four ids on two lines.
  • Dark controls: the // comment with an id inside the frozen FIELD_KEY_GUIDANCE table (authoring-key-lint.ts:100-101) and the docblock at driver/common.zod.ts:48-53 (two ids) read 0 hits; over the whole tree, 0 hit lines fall on a comment line. Parse diagnostics 0.
  • Independent cross-check: check-doc-authoring.mjs --census's own per-literal leg, pointed at packages/spec/src in a scratch copy (its root and exclusion constants changed, nothing else): 19 files, 441 id occurrences, 0 per-file differences from this census.
  • Word forms (PR / issue / card plus a number): every hit also carries a # id; none is extra.

Non-test sources: 230 messages, 441 id occurrences, in 19 files. Classified by audience:

class messages ids where
(a) author-visible refusal or prescription 13 14 data/authoring-key-lint.ts 4, data/driver.zod.ts 4, data/filter.zod.ts 2, data/object.zod.ts 2, data/driver/common.zod.ts 1. This PR.
(b) text shown to authors or administrators that is not a refusal: ADR-0087 conversion summaries 91 108 conversions/registry.ts, the summary of each conversion, printed in docs/protocol-upgrade-guide.md and in os migrate meta --json's specChanges
(b) the same: schema and route descriptions 3 3 data/field.zod.ts 1 (a .meta() description), api/plugin-rest-api.zod.ts 2 (route descriptions)
(c) conformance-case notes 58 68 the filter-logic, filter-text, filter-comparand-type, aggregation, temporal, value-roundtrip and metadata-service-roundtrip conformance modules and the text-operator declared-type table
(d) log lines 0 0
(f) internal registry rationale: shipped data whose reader is a contributor or a gate, never shown to an author 17 33 api/error-code-ledger.zod.ts 14 (the STANDARD_SYNONYM_WAIVERS and PROVENANCE_WAIVERS reasons), kernel/public-auth-features.ts 3
excluded: migrations/registry.ts (#20234's stage 11) 48 215

Test files, counted apart (class (e)): 1803 messages, 1919 id occurrences, in 425 files: about 1702 messages (1812 ids) in test titles and 101 (107) elsewhere.

Two class (c) facts a later stage needs: packages/lint/src/validate-empty-combinators.test.ts:275 SELECTS FILTER_LOGIC_CASES by note.includes('#5322') (4 notes, 7 ids: filter-logic-conformance.ts:420, :426, :432, :438); and packages/services/service-analytics/src/__tests__/icontains-dialect-sql.test.ts:369 pins one case NAME verbatim, id included (filter-text-conformance.ts:300, #8934).

The census as a whole, file:line, id and class, is in this stage's dev report on #20749. The card's census (175 messages at 36d043be17, envelope 26, prose 149) classified by syntax; this one classifies by who reads the text, so the two do not map one to one. For instance, the 14 waiver reasons in error-code-ledger.zod.ts, which a syntactic instrument files as refusal envelopes, are contributor-facing ledger rationale here, class (f).

What this does: the 13 class (a) messages

These are the texts an author meets at the moment something is refused or rewritten: the unknown-field-key guidance that os validate and the authoring lint print, the parse errors for retired DriverCapabilities keys, the guidance for readOnly written inside a datasource driver's config, the INVALID_FILTER refusal every driver face prints for $regex / $options, and the warning enable.apiMethods prints when it strips a retired legacy value. In form D, as stages 1 and 2 applied it, the number goes; where the sentence did not already say what was decided, it now does. Every cited card was read through REST, body and every comment.

Where Cited Decision read from The text now says
authoring-key-lint.ts:104, index guidance 2377 the card body and its closing record 5051634768: author-facing keys that pass validation but have no runtime consumer are removed (ADR-0049 enforce-or-remove); field index went in the follow-up slice "were removed in the 16.x line under ADR-0049 enforce-or-remove, which deletes a key no runtime reads"
authoring-key-lint.ts:107, indexed guidance 2377 as above "the field-level index flag built no index and was removed for it"
authoring-key-lint.ts:123, dataQuality guidance 3726 the card body (route 1: delete the orphaned DataQualityRules schema export, as the four sibling keys were) and landing comment 5098751722 on #3733 (both orphans deleted in one PR, route 1) "and its leftover DataQualityRules schema was deleted from the public API too"
authoring-key-lint.ts:133, cached guidance 3733 landing comment 5098751722 (route 1: ComputedFieldCache deleted; caching returns only with a consumer, the ADR-0049 enforce side) "its leftover ComputedFieldCache schema was deleted with it; nothing read it, and it returns only together with a runtime consumer"
driver.zod.ts:301, :305, :309, the bulkCreate / bulkUpdate / bulkDelete tombstones 3298 the card body (discovery advertises an atomic-batch capability bit so a client negotiates instead of probing for 404 / 405 / 501) and its landing bfa3c3fd59 (the transactionalBatch bit) "advertised by REST discovery as its transactionalBatch bit, derived from the live composition so a client negotiates instead of probing"
driver.zod.ts:361, the fullTextSearch tombstone 7641 PM ruling 5261610811 (option A: $search compiles to $icontains; $contains stays case-sensitive, untouched) "textual search is case-insensitive by ruling, while $contains itself stays case-sensitive"
driver/common.zod.ts:60, READ_ONLY_BELONGS_ON_DATASOURCE 4584 PM ruling 5163028174, the maintainer's veto unexercised (option B: no platform read-only gate; read-only is the database account's privilege, because a flag that only ObjectQL checks cannot stop direct connections, migrations or DDL) "the platform offers no read-only gate, by decision: a flag only the application checks cannot stop direct connections, migrations or DDL"
filter.zod.ts:3262, the $regex refusal 4706 the maintainer's ruling recorded in 5199214776 (option B: $regex retired under ADR-0049 with a loud refusal naming the replacement, $icontains added; a real regex on all five backends rejected) "is retired under ADR-0049 enforce-or-remove: it is refused here, never reinterpreted"
filter.zod.ts:3277, the $options refusal 4706 as above "which is retired under ADR-0049 enforce-or-remove"
object.zod.ts:57, the restore strip prescription 2377, 3146 2377 as above (enable.trash removed, no runtime reader); 3146 is open and labelled status:parked (a platform recycle bin, not scheduled) "(enable.trash was retired because no runtime ever read it); it returns only with a real recycle bin, and that soft-delete work is parked"
object.zod.ts:58, the purge strip prescription 2377 as above "(enable.trash was retired because no runtime ever read it)"

No occurrence was left in place: no cited decision was unclear.

Quoted elsewhere

  • content/docs/deployment/validating-metadata.mdx:320-322 quoted the indexed guidance verbatim, (#2377) included; the quote is updated in this PR.
  • content/docs/references/data/driver.mdx, driver-sql.mdx and driver-nosql.mdx carry the DriverCapabilities tombstones; they are generated, and check:generated --fix regenerated them (the one artifact it proved stale, check:docs).
  • skills/**: no quote of a changed message.
  • Other hits for these numbers are their own prose with their own citations (validation-rules.mdx, the apimethods-legacy-to-primitives.mjs codemod's docblock, source comments), not quotes; untouched.

Changeset

.changeset/20749-spec-strings-stage3-state-the-decision.md: patch for @objectstack/spec, carrying Clause-②: no. After the build, packages/spec/dist carries the new sentences and none of the replaced id-bearing fragments; the (#2377, ADR-0049) and (#3146, parked) hits left in dist are source comments and TSDoc that the build keeps, not these strings.

Text-only proof

Stage 1's tool, unchanged except the path it loads TypeScript from: a TypeScript-AST skeleton of each changed source in which every string literal and template text is a placeholder, a + chain is flattened and a run of adjacent string operands is one string (only its embedded expressions are kept, in order), identifiers, numbers and regex literals keep their text, every child is visited, and comments are never read. A second leg compares the TEXT of every string group in order: each group that changed must have carried a tracker id before and carry none after, and every other group must be byte-identical. 85e29b8858 against the head: 6 of 6 SAME on both legs (authoring-key-lint.ts, driver.zod.ts, driver/common.zod.ts, filter.zod.ts, object.zod.ts, object.test.ts), token counts identical per file, 14 groups changed (4, 4, 1, 2, 2, 1), all id-bearing before and id-free after, parse diagnostics 0/0.

Controls on scratch copies of object.zod.ts (head version), each mutation counted on disk first (1 anchor hit, replacement present, anchor gone, file differs): a function renamed reads DIFF (exit 1); === flipped to !== reads DIFF (exit 1); one literal re-split into two + operands reads SAME with no extra group changed (exit 0); a text change in a string that never carried an id reads SAME on the skeleton and VIOLATION on the text leg (exit 1). No repo file was mutated for the controls.

Pins

  • packages/spec/src/data/object.test.ts:2545 ("restore/purge strip carries the retired-trash guidance") asserted toContain('#2377'); it now asserts toContain of "enable.trash was retired because no runtime ever read it", the same strength on the words that replaced the number.
  • No other test asserts a changed phrase: every replaced fragment and every distinctive unchanged phrase of the 13 messages was searched across all test files. The driver and having-filter refusal tests assert $icontains, $regex and RETIRED, which stay, or compare against RETIRED_FILTER_OPERATORS[op].why by reference.

Tests

All builds and tests through scripts/pm/os-verify-lock.sh, each VERDICT command-exit 0.

  • pnpm --filter @objectstack/spec build (it has no workspace dependencies, so the closure is the package itself); then pnpm turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/* ("Tasks: 71 successful, 71 total") for the dist-reading gates. The tree was clean after both builds.
  • @objectstack/spec test (the local project), vitest run --project local --maxWorkers=2: "Test Files 602 passed (602) / Tests 17787 passed | 1 todo (17788)".
  • The repo project: the 11 files that read the changed sources or the regenerated docs, plus the src/data ones, "Test Files 11 passed (11) / Tests 258 passed (258)". 38 of the other 40 were run too, and 561 of their tests had passed with 0 failed when a 420 s wall-clock cut stopped the run in the slowest file, scripts/build-schemas-check-mode.test.ts; the two publish-smoke files were not run. That remainder is not measured here and is CI's.
  • @objectstack/spec typecheck: tsc --noEmit exit 0, check:scripts-typecheck exit 0, and "check:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json; 52 file(s) / 246 error(s) / 135 pinned signature(s) held".
  • ESLint on the six changed TypeScript files, eslint --no-inline-config --format json: 6 files, 0 errors, 0 warnings. That narrowing is complete for them: ESLint's own config resolves for each (none ignored), and this repo's config enables no type-aware linting (parserOptions.project and projectService absent), so these edits cannot move any untouched file's verdict. The repo-wide pnpm lint run is CI's.

Gates

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) at 77a448f8cc, change set 11 paths against the merge base 85e29b885: 110 commands, run one at a time from the worktree, each exit code written before any pipe. --ran: "110 derived famil(ies) accounted for — 110 run, 0 NOT-MEASURED (a DERIVED zero — all 110 recorded an exit code and none of them is 3)". All 110 exit 0; the dist-reading ones (check:generated, check:docs-transcript-drift, check:dts-closure, check:dual-build-cjs-loads, check:lean-entry-closure, check:published-files, check:published-readme-links, check:sourcemap-no-sources-content) after the full build.
  • Outside the derived set, also all exit 0: the 11 declared wide-population families, and the artifact-roster families whose roster sits under one of this PR's directories or reads its subject (check-changeset-fixed, check:meta-url-spelling, check:spec-changes, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:error-code-provenance, check-published-list-mirrors, check:published-readme-exports).
  • Named by the dispatch: @objectstack/spec check:generated exit 0 ("All 15 generated artifacts are up to date"); pnpm check:doc-authoring (self-test and run) exit 0 ("17287 customer-facing string(s) across 1246 spec sources clean" and "sibling-package prose ids hold the baseline — 72 pinned site(s) across 21 file(s) ... no growth, no burn-down unrecorded"); pnpm check:nul-bytes exit 0; check-adr-0087-registration exit 0 ("this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)"); check:empty-changeset exit 0; check-changeset-fixed exit 0; check-changeset-no-major exit 0 on the plain run and when fed this body as a pull_request event; check:partof-closing-keyword on this body exit 0.
  • check-issue-citations: "no issue citations added against 85e29b8 (5 file(s) read)". The prose-id ledger is untouched: packages/spec is outside it, and no gate is added or loosened.
  • main moved three commits past the merge base while this ran (2ee8383f4e, 25797a16e1, f9a8eb889e); none touches packages/spec, content/docs or any file of this PR, so no merge was made.

Acceptance notes

Noted, not filed:

  • The census leaves, for later stages, in the ruling's order: (b) 91 conversion summaries (108 ids) and 3 descriptions; (c) 58 conformance notes (68 ids), of which 4 are a test selector and 1 a pinned case name; (f) 17 registry rationales (33 ids); (e) about 1803 test strings (1919 ids); and the excluded migrations/registry.ts (48 messages, 215 ids) on packages/spec/src: 1,277 comment lines still cite 170 deleted tracker numbers (1,295 sites) — the staged remainder of ruling C+D on #19123, measured by PR #20226 #20234's stage 11.
  • check:doc-authoring's spec leg read green on main over all 13 of these messages. It recognises a retiredKey() prescription and a builder whose return feeds one, but not a string passed as an ARGUMENT to such a builder (retiredKey(capRemoved(key, '...'))), and its hoisted-const pass is per module, so a guidance table consumed from another module (READ_ONLY_BELONGS_ON_DATASOURCE, the why of FIELD_KEY_GUIDANCE and RETIRED_FILTER_OPERATORS) sits outside it. LEGACY_API_METHOD_GUIDANCE is not one of these: it is defined and consumed in its own module (object.zod.ts:52 and :123), and why the leg passed it is not measured here. With this PR the population of those shapes is empty; the gap stays for the next one. No gate is changed here, by ruling.
  • Comments in these files keep their ids: a comment is the sanctioned home for an internal anchor.

Seat edit, 2026-10-03T02:37Z: the check:doc-authoring note no longer lists LEGACY_API_METHOD_GUIDANCE as consumed from another module. Contract review 5964640454 read it as consumed only in object.zod.ts.


Generated by Claude Code

claude added 3 commits October 3, 2026 01:05
…ion in words instead of a tracker number

The five spec sources whose refusal and prescription text an author meets at
parse, lint or query time (field-key guidance, the DriverCapabilities
tombstones, the datasource readOnly guidance, the retired filter operators and
the legacy apiMethods strip warning) no longer cite tracker numbers. Each
sentence states what was decided, or drops a citation it already explained.
Text only. One test pin and one docs quote move with the text.

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
…apability tombstones

check:generated --fix, the one artifact it proved stale (check:docs).

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
…d prescription text

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 2 changed file(s) yielded no anchor (packages/spec/src/data/authoring-key-lint.ts, packages/spec/src/data/filter.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/src/data/authoring-key-lint.ts, packages/spec/src/data/filter.zod.ts) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 138 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 c205b6c350a2cf5c8efb614c70dc0e5b8efc36ee → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8e5a93012882bf571f3d4d16a93dc1e3d60e2ad3 — the merge of head 77a448f8cc4b8cc66e79153a792b9d25291fdadc into base c205b6c350a2cf5c8efb614c70dc0e5b8efc36ee, 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 8e5a93012882bf571f3d4d16a93dc1e3d60e2ad3 && git checkout 8e5a93012882bf571f3d4d16a93dc1e3d60e2ad3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c205b6c350a2cf5c8efb614c70dc0e5b8efc36ee 77a448f8cc4b8cc66e79153a792b9d25291fdadc && git checkout -B drift-repro c205b6c350a2cf5c8efb614c70dc0e5b8efc36ee && git merge --no-ff 77a448f8cc4b8cc66e79153a792b9d25291fdadc

node scripts/docs-audit/affected-docs.mjs --json c205b6c350a2cf5c8efb614c70dc0e5b8efc36ee

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 77a448f8cc4b8cc66e79153a792b9d25291fdadc
Local-runs: none

① Derived judgments

Inputs. Read on GitHub: card #20749 (body and every comment, the stage-3 claim 5963811219 and dev report 5964505246 included), ruling 5902360492 on #20513, the eight cited cards (#2377, #3146, #3298, #3726, #3733, #4584, #4706, #7641) with every comment, PR #21521 (body, file list, net diff against main, its one comment) and this head's check-runs. Repository reads were git show / git grep at origin/main, the merge base 85e29b8858 and this head; nothing was built, run or re-run. An isolated subagent of the seat session rendered this record.

The 13 rewritten messages, each against the cited card's decision. All 13 state the decision truly, keep the message's meaning and prescription, and carry no tracker number (every added line of the diff was swept for a # followed by 3 to 5 digits: none).

Message Cited Read against Judgment
FIELD_KEY_GUIDANCE.index #2377 the card body (ADR-0049's remove arm: author-facing keys with no runtime consumer) and its closing record 5051634768 (index removed in the follow-up slice) True. "under ADR-0049 enforce-or-remove, which deletes a key no runtime reads" names the arm that applied. Prescription unchanged: declare the index in indexes[].
FIELD_KEY_GUIDANCE.indexed #2377 as above True: indexed was never a key, and the field-level index flag built no index and went in that slice. Prescription unchanged.
FIELD_KEY_GUIDANCE.dataQuality #3726 the card body (route 1: delete the orphaned DataQualityRules export) and #3733's landing 5098751722 (both orphans deleted in PR #3732, route 1) True; "it enforced nothing" kept.
FIELD_KEY_GUIDANCE.cached #3733 landing 5098751722 (ComputedFieldCacheSchema deleted; key and schema return only together with a consumer, the ADR-0049 enforce side) True.
bulkCreate / bulkUpdate / bulkDelete tombstones #3298 the card body (a discovery bit so clients negotiate instead of probing 404 / 405 / 501) and its landing bfa3c3fd59 True, and the live-composition claim is verified on origin/main: rest-server.ts sets capabilities.transactionalBatch.enabled as the protocol's engineCanRollBack(engine) AND the mounted /batch route (api.enableBatch); discovery.zod.ts declares the bit. The capRemoved prescription "Delete the key" is unchanged.
fullTextSearch tombstone #7641 PM ruling 5261610811, option A: $search compiles to $icontains; $contains stays contractually case-sensitive True on both halves.
READ_ONLY_BELONGS_ON_DATASOURCE #4584 ruling 5163028174: 「方案 B —— 不建平台层只读闸门,文档明确记录」, because a bit that blocks only the ObjectQL write path 「拦不住直连/迁移/DDL」 and GRANT SELECT is the real boundary; executed in PR #7241, card closed completed, the maintainer's veto unexercised True. "by decision" is what the ruling is: a standing ruling of record, executed and closed. The three things named (direct connections, migrations, DDL) are the ruling's three. Prescription (SELECT-only at the database) unchanged; the FEDERATED half (external.allowWrites: false) unchanged.
RETIRED_FILTER_OPERATORS.$regex.why #4706 ruling 5199214776 (maintainer 「同意你的建议」): 「$regex 按 ADR-0049 退役(查询校验期响亮拒收,信息点名替代物)」, option A (real regex on all backends) excluded True. "refused here, never reinterpreted" matches the driver faces: driver-sql's retiredFilterOperatorError raises a RETIRED refusal carrying guidance.why verbatim plus "Write $icontains instead"; no face compiles an author's $regex (the $regex spellings inside driver-memory and driver-mongodb are those drivers' own internal compiled form; turso's case '$regex' is a temporal-coercion pass-through, not a compile arm).
RETIRED_FILTER_OPERATORS.$options.why #4706 as above True; prescription ($icontains) unchanged.
LEGACY_API_METHOD_GUIDANCE.restore #2377, #3146 5051634768 (enable.trash removed in #3414) with #3146's body (the liveness audit: zero behaviour-changing readers); #3146 is open, labelled status:parked, no comments True: "retired because no runtime ever read it" is the audit's finding; "that soft-delete work is parked" is #3146's label. Prescription "delete the value" unchanged.
LEGACY_API_METHOD_GUIDANCE.purge #2377 as above True.

Text only. Every hunk of the five sources is read: only string-literal text and +-concatenation boundaries move; no key, schema shape, condition, error code, status or code path. object.test.ts:2545 re-pins the words that replaced the number at the same strength (toContain), and the strip warning is built from LEGACY_API_METHOD_GUIDANCE[v], so the new pin matches the emitted text. The docs quote in validating-metadata.mdx equals the new indexed text; the three generated driver references mirror the four tombstones. The prose-id ledger is untouched, correctly: packages/spec sits outside it and no ledgered file changes. The old fragments survive at this head only in two comments (scripts/codemod/apimethods-legacy-to-primitives.mjs:39-40 and api-derivation.ts:138), which the ruling's three categories do not reach.

Census class-(a) boundary. Sound. The 13 are the texts printed at the moment of a refusal, a strip or a parse error, and each sink is verified on origin/main: FIELD_KEY_GUIDANCE feeds field.zod.ts's strictObject options and metadata-authoring-lint.ts; READ_ONLY_BELONGS_ON_DATASOURCE is the readOnly guidance in six driver config schemas; RETIRED_FILTER_OPERATORS[op].why is printed verbatim by the sql, memory, mongodb and turso faces; the tombstones are retiredKey() parse errors; LEGACY_API_METHOD_GUIDANCE is the strip warning. The 91 conversion summaries are rightly NOT class (a): a summary surfaces in the generated upgrade-guide table (build-upgrade-guide.ts:101) and the migrate meta report, describing a rewrite already applied, while the load-path notice itself (conversions/apply.ts) prints its own prescription and no summary; they are shown to authors, so class (b) and a later stage. Class (f) is a fair reading: the only references to STANDARD_SYNONYM_WAIVERS / PROVENANCE_WAIVERS outside the ledger module are comments in two route files, and the PUBLIC_AUTH_FEATURES entries' reason / notes are read only inside their module and by a dogfood ledger; no author or administrator is shown them.

② Semver level

@objectstack/spec patch, Clause-②: no — right. Author-visible message text changes with no key, shape, export or acceptance change; no published surface grows or narrows. The changeset states in words that a matcher on the old text needs the new spelling. The PR body carries Clause-②: no at a line start, Part of #20749, and no closing keyword.

③ Boundary flags

  1. Sixth class (f) — a fair reading, verified above. In scope as shipped runtime strings under the ruling's first category; its staging (last, with the log lines) is the seat's call, and the dev named the call rather than folding it.
  2. Conversion summaries as class (b) — sound. Had they been (a), the A6 stop would have tripped, and the dev said so. They are their own stage or stages.
  3. Re-implemented census instrument — the [finding] runtime warnings outside the migration ledger print tracker numbers to authors and operators: the AutomationEngine resumeAuthority boot warning (#3801 / #5561 / #3823) and two objectql data-event warnings (#4639 / #4626) #20513 instrument was not on disk, so it was rebuilt from its stated semantics with one extra folding rule, lit and dark controls, and a 441 = 441 / 0-per-file cross-check against check-doc-authoring.mjs --census's own per-literal leg pointed at packages/spec/src. Not re-run here; its authority is the report. A later stage re-taking the census should hold itself to the gate's leg the way this one did.
  4. Proof tool — stage 1's skeleton.cjs with one line changed (the TypeScript load path), both md5s stated. Not re-run here; the diff is small enough that the text-only finding above rests on reading every hunk.
  5. Partial test:repo — the package test project ran whole (602 files), 11 relevant repo-project files ran whole, 38 more to a 420 s cut with 0 failures, and 2 publish-smoke files were not run. CI is the measurement: at 2026-10-03T02:32Z this head reads 31 success, 2 skipped by design (Console Pin Gate, the opt-in packed-tarball smoke), 1 in progress (Test Core 2/6). An in-progress shard is not a pass; landing waits for it.
  6. Acceptance note overstates one mechanism — the body's check:doc-authoring note lists LEGACY_API_METHOD_GUIDANCE among tables "consumed from another module"; on origin/main it is consumed only in object.zod.ts:123, inside the strip warning's builder. The dev's deviation says so; the body is unpatched, and the seat may correct it — nothing in the diff depends on it. Likewise the report's "four driver modules" for READ_ONLY_BELONGS_ON_DATASOURCE undercounts: six at the merge base (memory, mongo, mysql, postgres, sqlite, turso). Immaterial: the constant is defined once and the diff changes that one definition.
  7. Out-of-scope finding, check:doc-authoring's spec leg — consistent with the gate's own header: TOMBSTONE_CALLS reads argument 0 of retiredKey(...), which here is a call, and the hoisted-const pass is per module. "Noted, not filed" and folded into the family is the seat's call; since the shape recurs with the next tombstone written through a builder, the seat should decide file-or-fold explicitly at the lane's end. No gate changed here, by ruling — correct.
  8. Out-of-scope finding, the two test seams — both verified on origin/main (validate-empty-combinators.test.ts:275 selects by note.includes('#5322'); icontains-dialect-sql.test.ts:369 pins the #8934 case name verbatim). Rightly outside this stage per the claim's ⛔ list; the class (c) stage must move them together with their tests.
  9. Governed surface — NOT governed (Governed Surface Queue Guard success; no .claude/**, skills/**, ADR, NORTH-STAR or AGENTS.md path). This review is owed by the seat's own claim bar for packages/spec/src runtime text, not by Tier S; landing is the ordinary queue once every check is green.
  10. Serial constraints — migrations/registry.ts (packages/spec/src: 1,277 comment lines still cite 170 deleted tracker numbers (1,295 sites) — the staged remainder of ruling C+D on #19123, measured by PR #20226 #20234 stage 11), the conformance selectors, test titles and the two .mjs gate scripts are untouched, as the claim required.

Implemented-by: claude/issue-20749-spec-strings-stage3
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #21521 @ 77a448f8 (stage 3 of #20749)

domain:spec seat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim 5963811219 · 2026-10-03T02:38Z

  • Shape (read on GitHub): a draft against main. The first line is Part of #20749, and Clause-②: no sits at a line start, with no closing keyword. 11 files, +60 / −36: five packages/spec/src/data sources carrying the 13 class (a) messages, one test pin and one docs quote that moved with the text, three generated driver reference pages, and one changeset.
  • Review: the contract review at CONTRACT_REVIEW_TIER, 5964640454, reads PASS on this head.
    • All 13 rewritten messages state the cited decision truly, keep their meaning and their prescription, and carry no tracker number. The reviewer re-checked each decision on origin/main, among them: discovery's transactionalBatch derivation, ruling 5163028174 (option B, no platform read-only gate), the $regex / $options refusal on every driver face, and Implement soft delete (recycle bin): enable.trash / softDelete are spec-only with zero runtime readers #3146's parked state.
    • Text only: every hunk was read. Only string text and + boundaries move. The stage-1 AST-skeleton proof reads 6 of 6 SAME.
    • The census classes hold: class (a) is the author-visible refusals. The conversion summaries are rightly class (b), and the contributor-facing rationale is fairly a class (f).
    • Release: an @objectstack/spec patch with Clause-②: no, which is right.
  • Seat corrections, made in this act:
    • The PR body's check:doc-authoring note listed LEGACY_API_METHOD_GUIDANCE as consumed from another module. It is defined and consumed in object.zod.ts (:52, :123). The body is patched, with an edit note. The diff is unchanged.
    • The dev report on the card says four driver modules import READ_ONLY_BELONGS_ON_DATASOURCE. At origin/main the importers are six: memory, mongo, mysql, postgres, sqlite and turso. Immaterial to the diff; recorded here.
  • CI on 77a448f8: 33 success and 2 skipped by design (Console Pin Gate, packed-tarball smoke). The body patch re-runs the body-reading checks, and the queue waits for them.
  • Governed surface: check-governed-merges --pr 21521 reads NOT governed: 0 of 11 paths, 96 changed lines.
  • Serial: at this read, no other open PR touches the five packages/spec/src/data sources.

Carried, decided here:

  • Class (f), the sixth class, is accepted as a staging bucket. The waiver reasons in error-code-ledger.zod.ts and the public-auth-features.ts reasons are read by comments and tests outside their modules, never by an author. They are staged last in this lane, after class (c).
  • The conversion summaries are their own stage: 91 messages, 108 ids, all in conversions/registry.ts. They print in the generated upgrade guide and in os migrate meta.
  • Two class (c) test seams are left for the class (c) stage, which must change them with the notes: validate-empty-combinators.test.ts:275 (a selector over #5322 notes) and icontains-dialect-sql.test.ts:369 (a pinned case name).
  • The check:doc-authoring spec-leg blind spot (a string passed as an argument to a retiredKey() builder, and a guidance table consumed from another module). After this PR, its population is empty. It is held to the lane's end, where this seat decides whether to file it or fold it. No gate changes here, by ruling.

Staging after this lands: in packages/spec/src, class (b) (the conversion summaries, then 3 descriptions), class (c) (58 conformance notes), then class (f) (17). Test strings are counted apart (1,803).

Next: ready, auto-merge, the queue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 02:40
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 02:40
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit ad7c351 Oct 3, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20749-spec-strings-stage3 branch October 3, 2026 03:04
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 protocol:data size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants