Skip to content

Commit ad7c351

Browse files
fix(spec): author-visible refusals and prescriptions state each decision in words instead of a tracker number (stage 3) (#21521)
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 `reason`s 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 #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](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent e3ad492 commit ad7c351

11 files changed

Lines changed: 60 additions & 36 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
Field-key guidance, the retired `DriverCapabilities` tombstones, the datasource `readOnly` guidance, the retired filter operators and the legacy `apiMethods` strip warning no longer cite tracker numbers; each one states the decision behind it in words
6+
7+
Clause-②: no
8+
9+
These are the `@objectstack/spec` texts an author meets at the moment something is refused or rewritten: the unknown-field-key guidance that `os validate` and the 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. They pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
10+
11+
- Field-key guidance: `index` and `indexed` say the field-level index flag built no index and was removed under ADR-0049 enforce-or-remove; `dataQuality` and `cached` say their leftover `DataQualityRules` and `ComputedFieldCache` schemas were deleted from the public API too, and that computed-field caching returns only together with a runtime consumer.
12+
- `DriverCapabilities` tombstones: the `bulkCreate` / `bulkUpdate` / `bulkDelete` prescriptions name discovery's `transactionalBatch` bit, derived from the live composition so a client negotiates instead of probing; the `fullTextSearch` prescription says `$contains` itself stays case-sensitive while textual search is case-insensitive.
13+
- Datasource `readOnly` guidance: says a managed datasource has no read-only gate by decision, because a flag only the application checks cannot stop direct connections, migrations or DDL.
14+
- Retired filter operators: the `$regex` and `$options` refusals say they are retired under ADR-0049 enforce-or-remove, refused rather than reinterpreted.
15+
- Legacy `apiMethods` strip warning: the `restore` and `purge` prescriptions say `enable.trash` was retired because no runtime ever read it, and that the recycle-bin (soft-delete) work `restore` would need is parked.
16+
17+
Text only: no key, schema shape, condition, error code or status moves. A tool or test that matches the old text (for example a tracker-number suffix) needs the new spelling.

‎content/docs/deployment/validating-metadata.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ objects.employee.fields.ssn.pii: 'pii' is not a declared field key, so its value
318318
is dropped at load — the `dataQuality` governance family was pruned in 2026-06
319319
as dead in both layers — it enforced nothing.
320320
objects.employee.fields.ssn.indexed: 'indexed' is not a declared field key, so its
321-
value is dropped at load — never a FieldSchema key; a field-level index flag
322-
built no index (#2377). Declare the index in the object's `indexes[]`.
321+
value is dropped at load — never a FieldSchema key; the field-level `index` flag
322+
built no index and was removed for it. Declare the index in the object's `indexes[]`.
323323
```
324324

325325
Plain typos get a "did you mean" (`requred` → `required`); a retired key does

‎content/docs/references/data/driver-nosql.mdx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ const result = AggregationPipelineSchema.parse(data);
169169
| **read** | `never` | optional | [REMOVED] `DriverCapabilities.read` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. CRUD is not optional for a driver: reads go through the REQUIRED `find`/`findOne`/`count` methods, called unconditionally. Delete the key. |
170170
| **update** | `never` | optional | [REMOVED] `DriverCapabilities.update` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. CRUD is not optional for a driver: `update`/`upsert` are REQUIRED `IDataDriver` methods, called unconditionally. Delete the key. |
171171
| **delete** | `never` | optional | [REMOVED] `DriverCapabilities.delete` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. CRUD is not optional for a driver: `delete` is a REQUIRED `IDataDriver` method, called unconditionally. Delete the key. |
172-
| **bulkCreate** | `never` | optional | [REMOVED] `DriverCapabilities.bulkCreate` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods (`bulkCreate`/`bulkUpdate`/`bulkDelete`) are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery from the live composition (#3298), never from this record. Delete the key. |
173-
| **bulkUpdate** | `never` | optional | [REMOVED] `DriverCapabilities.bulkUpdate` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery from the live composition (#3298), never from this record. Delete the key. |
174-
| **bulkDelete** | `never` | optional | [REMOVED] `DriverCapabilities.bulkDelete` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery from the live composition (#3298), never from this record. Delete the key. |
172+
| **bulkCreate** | `never` | optional | [REMOVED] `DriverCapabilities.bulkCreate` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods (`bulkCreate`/`bulkUpdate`/`bulkDelete`) are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery as its `transactionalBatch` bit, derived from the live composition so a client negotiates instead of probing, never from this record. Delete the key. |
173+
| **bulkUpdate** | `never` | optional | [REMOVED] `DriverCapabilities.bulkUpdate` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery as its `transactionalBatch` bit, derived from the live composition so a client negotiates instead of probing, never from this record. Delete the key. |
174+
| **bulkDelete** | `never` | optional | [REMOVED] `DriverCapabilities.bulkDelete` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. The bulk methods are REQUIRED `IDataDriver` methods and the engine calls them directly; wire-level batch capability is advertised by REST discovery as its `transactionalBatch` bit, derived from the live composition so a client negotiates instead of probing, never from this record. Delete the key. |
175175
| **transactions** | `never` | optional | [REMOVED] `DriverCapabilities.transactions` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. Transaction use is gated on the DRIVER'S DECLARATION, no longer on METHOD PRESENCE alone: `engine.transaction()` asks `driverSupportsTransactions(driver)` — `driver.beginTransaction` present AND `transactionsUnsupported` not set (ADR-0034 ambient transactions, ADR-0119 D1). A driver without the method — or a transport that declares that live bit — gets the non-transactional fallback, whatever this bit claimed. Discovery's `transactionalBatch` capability is likewise derived from `engine.transaction` plus the mounted batch route, never from this bit. The live `transactionsUnsupported` bit is NOT this key restored and is not its opposite spelled differently: this one CLAIMED support nothing checked, that one DENIES support the engine does check, and it is written only by a transport that inherits `beginTransaction` from a base class it cannot honour. A driver with real transactions declares nothing. Delete the key. |
176176
| **savepoints** | `never` | optional | [REMOVED] `DriverCapabilities.savepoints` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. No savepoint code path exists in the engine — a capability bit for a feature the platform does not call is a false affordance, not documentation. Delete the key. |
177177
| **isolationLevels** | `never` | optional | [REMOVED] `DriverCapabilities.isolationLevels` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. Isolation is requested per transaction via `beginTransaction({ isolationLevel })`; no planner ever consulted this list to decide anything. Delete the key. |
@@ -183,7 +183,7 @@ const result = AggregationPipelineSchema.parse(data);
183183
| **querySubqueries** | `never` | optional | [REMOVED] `DriverCapabilities.querySubqueries` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. ObjectQL never plans subqueries through a driver, so there was nothing for the bit to switch on. Delete the key. |
184184
| **queryCTE** | `never` | optional | [REMOVED] `DriverCapabilities.queryCTE` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. ObjectQL never plans Common Table Expressions through a driver, so there was nothing for the bit to switch on. Delete the key. |
185185
| **joins** | `never` | optional | [REMOVED] `DriverCapabilities.joins` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. Related data is resolved by the engine (lookup expansion over `find()`), not by driver-side JOIN planning — no code consulted the bit. Delete the key. |
186-
| **fullTextSearch** | `never` | optional | [REMOVED] `DriverCapabilities.fullTextSearch` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. `$search` is compiled by the engine into an `$or` of `$icontains` predicates over the searchable fields (ADR-0061) and removed from the AST before the driver sees it — no driver-side full-text path exists. The operator is `$icontains`, NOT `$contains`: textual search is case-insensitive by ruling (#7641). Delete the key. |
186+
| **fullTextSearch** | `never` | optional | [REMOVED] `DriverCapabilities.fullTextSearch` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. `$search` is compiled by the engine into an `$or` of `$icontains` predicates over the searchable fields (ADR-0061) and removed from the AST before the driver sees it — no driver-side full-text path exists. The operator is `$icontains`, NOT `$contains`: textual search is case-insensitive by ruling, while `$contains` itself stays case-sensitive. Delete the key. |
187187
| **jsonQuery** | `never` | optional | [REMOVED] `DriverCapabilities.jsonQuery` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. No engine path ever branched on driver-side JSON querying. Delete the key. |
188188
| **geospatialQuery** | `never` | optional | [REMOVED] `DriverCapabilities.geospatialQuery` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, so its value never changed which code path ran. No geospatial query path exists in the platform — declaring the bit advertised a capability nothing delivers. Delete the key. |
189189
| **streaming** | `never` | optional | [REMOVED] `DriverCapabilities.streaming` was removed in @objectstack/spec 17.0.0 (ADR-0049 enforce-or-remove) — no code in any repository ever read it, and `findStream`, the only read this bit could describe, was itself removed in 17.0.0: nothing ever called it, and two of its three implementations materialised the entire result set before yielding. The bit carried the same defect one level up (`SqlDriver` implemented `findStream` yet declared `streaming: false`; `InMemoryDriver` declared `true` over a full-table read) — which is what zero readers makes inevitable. Page large reads through `find()` with `limit`/`offset`. Delete the key. |

0 commit comments

Comments
 (0)