Skip to content

Commit 6f74eb4

Browse files
committed
chore(changeset): Clause-② yes (narrowing) for objectql and yes for spec; scope sentences to the measured drivers
The objectql changeset carries the PR's Clause-② line, yes (narrowing), and keeps its BREAKING banner. Its sentences are now scoped to what was measured (InMemoryDriver, SqlDriver over SQLite, through findData). The spec changeset reads Clause-② yes, with no banner, no arm and no ADR-0087 marker: the module is additive, and the narrowing is objectql's. Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1c184d7 commit 6f74eb4

2 files changed

Lines changed: 10 additions & 14 deletions

File tree

‎.changeset/21333-objectql-boolean-comparand-door.md‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,24 @@
44

55
fix(objectql)!: a comparand against a declared boolean field is narrowed to its boolean at the engine's filter door, and any string other than "true" / "false" / "1" / "0" is refused with `INVALID_FILTER` / 400
66

7-
Clause-②: no (narrowing)
7+
Clause-②: yes (narrowing)
88

9-
<!-- adr-0087: not-required (no-migration-prescription) a refusal and a narrowing of filter COMPARANDS at the engine's query door, against a declared boolean or toggle field. No authorable key, spelling, export or stored shape moves: every query shape, every FilterCondition and every object definition parse as before, @objectstack/objectql exports nothing new and nothing less, and no stored row is read or rewritten. What is refused is a comparand that matched no row (and every row under $ne) on every driver, and which boolean a caller meant by "yes" is not something a ledger entry can decide. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers a filter comparand, and this diff adds none (not registered / already-registered); and the change is runtime behaviour, not a declaration (not runtime-interface-only / type-surface-only). -->
9+
<!-- adr-0087: not-required (no-migration-prescription) a refusal and a narrowing of filter COMPARANDS at the engine's query door, against a declared boolean or toggle field. No authorable key, spelling, export or stored shape moves: every query shape, every FilterCondition and every object definition parse as before, @objectstack/objectql exports nothing new and nothing less, and no stored row is read or rewritten. What is refused is a comparand that matched no row (and every row under $ne) on InMemoryDriver and on SqlDriver over SQLite (PostgreSQL and MySQL not measured), and which boolean a caller meant by "yes" is not something a ledger entry can decide. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers a filter comparand, and this diff adds none (not registered / already-registered); and the change is runtime behaviour, not a declaration (not runtime-interface-only / type-surface-only). -->
1010

11-
**BREAKING**: this narrows what a filter may compare a declared `boolean` or `toggle` field with, at every filter position and through every door. It ships as `minor` under the launch-window convention for accept-set narrowings. No export or published type changes.
11+
**BREAKING**: this narrows what a filter may compare a declared `boolean` or `toggle` field with, at every filter position and through every door that reaches the engine's filter walk (`engine.find` / `findOne` / `count` / `aggregate` / `update` / `delete`, and every spelling the data API hands it). It ships as `minor` under the launch-window convention for accept-set narrowings. No export or published type changes.
1212

13-
**What was accepted before.** A string compared with a boolean field was neither refused nor read as a boolean: the engine handed it to the driver as written, and every answer was a 200. Measured on two rows (one `true`, one `false`) on InMemoryDriver and on SqlDriver over SQLite, through `engine.find`, `engine.aggregate` and `POST /api/v1/data/:object/query` / `GET /api/v1/data/:object`:
13+
**What was accepted before.** A string compared with a boolean field was neither refused nor read as a boolean: the engine handed it to the driver as written, and every answer was a 200. Measured on two rows (one `true`, one `false`) on InMemoryDriver and on SqlDriver over SQLite, through `engine.find`, `engine.aggregate` and the protocol's `findData` with each spelling the `POST /api/v1/data/:object/query` and `GET /api/v1/data/:object` routes hand it:
1414

15-
- `"true"` and `"false"` (implicit, `$eq`, `$in`, `?filter=`, `?$filter=`, the filter AST, and the bare query parameter `?flag=true`) matched no row on either driver;
15+
- `"true"` (implicit, `$eq`, `$in`) and `"false"` (implicit), and both through `?filter=`, `?$filter=`, the filter AST and the bare query parameter (`?flag=true`), matched no row on either driver;
1616
- `$ne "true"` and `$nin ["true"]` returned both rows, the true row included;
1717
- `"yes"` matched no row, and `$ne "yes"` both rows;
18-
- `1`, `"1"`, `0` and `"0"` matched the right row on SQLite and no row on InMemoryDriver (`$ne 1` returned both rows there);
18+
- `1`, `"1"`, `0` and `"0"` at `where` (and `"1"` / `"0"` through every spelling above) matched the right row on SQLite and no row on InMemoryDriver (`$ne 1` returned both rows there);
1919
- the per-aggregation `filter` and `having` (the engine's own evaluator) answered `"true"` with no row and no group, and `$ne "true"` with every one.
2020

2121
**What is answered now.** At `where` (both spellings), the per-aggregation `filter` and `having`, on every verb that collects a filter, before any driver is asked for a row:
2222

2323
- `true` / `false` are handed to the driver as written;
24-
- `1` / `0`, `"1"` / `"0"` and `"true"` / `"false"` are narrowed to `true` / `false`, so every driver receives the one boolean each names. `?flag=true` and `?flag=1` now return the true row on every driver;
24+
- `1` / `0`, `"1"` / `"0"` and `"true"` / `"false"` are narrowed to `true` / `false`, so every driver receives the one boolean each names. Measured on InMemoryDriver and on SqlDriver over SQLite, `?flag=true` and `?flag=1` now return the true row; any other driver receives the same narrowed boolean by mechanism (PostgreSQL and MySQL not measured);
2525
- any other string, a different letter case (`"TRUE"`), surrounding whitespace, a blank and a `{placeholder}` included, is refused `INVALID_FILTER` / 400. The message names the field, its declared type, the comparand and its position, and says what is wrong with it.
2626

2727
The accepted set is the one the record validator already admits when a boolean field is WRITTEN. The rule lives in `@objectstack/spec/data`'s `filter-boolean-comparand-declared-type.ts`, and the engine applies it in the same walk that judges number comparands.

‎.changeset/21333-spec-boolean-comparand-contract.md‎

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@
44

55
feat(spec): the boolean-comparand declared-type contract in `@objectstack/spec/data` — the comparands a declared boolean field accepts in a filter, the boolean each narrows to, and the refusal words
66

7-
Clause-②: no (narrowing)
8-
9-
<!-- adr-0087: not-required (no-migration-prescription) an additive contract module: new exports only, in a new file beside the number-comparand contract. No authorable key, spelling, existing export or stored shape moves, no Zod schema changes, and no stored row is read or rewritten. The narrowing it declares (a string other than "true" / "false" / "1" / "0" against a declared boolean field is refused in a filter) is enforced at the engine's query door by @objectstack/objectql, whose changeset states it; which boolean a caller meant by "yes" is not something a ledger entry can decide. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id covers a filter comparand, and this diff adds none (not registered / already-registered); and the module declares a runtime rule, not a TypeScript-only surface (not runtime-interface-only / type-surface-only). -->
10-
11-
**BREAKING**: the module declares a narrowing of what a filter may compare a declared `boolean` or `toggle` field with; the engine door that enforces it is `@objectstack/objectql`'s. It ships as `minor` under the launch-window convention for accept-set narrowings. Every existing export is unchanged.
7+
Clause-②: yes
128

139
**What it declares.** `filter-boolean-comparand-declared-type.ts`, the boolean twin of `filter-number-comparand-declared-type.ts`:
1410

@@ -18,6 +14,6 @@ Clause-②: no (narrowing)
1814
- `booleanComparandRefusalMessage`: the refusal words, inside the 500-character client bound.
1915
- `BOOLEAN_COMPARAND_READING_CASES`, `BOOLEAN_COMPARAND_DOOR_FIXTURE` and the derived `BOOLEAN_COMPARAND_DOOR_CASES`, for a door's suite to drive.
2016

21-
**What is refused.** A string other than the four accepted ones, compared with a declared boolean field, at the value positions of a filter (the implicit comparand, `$eq` / `$ne` / `$gt` / `$gte` / `$lt` / `$lte`, and each member of `$in` / `$nin` / `$between`). Before, such a string was compared as written and matched no row (every row under `$ne`).
17+
**What the verdict answers `door-refusal` for.** A string other than the four accepted ones, compared with a declared boolean field, at the value positions of a filter (the implicit comparand, `$eq` / `$ne` / `$gt` / `$gte` / `$lt` / `$lte`, and each member of `$in` / `$nin` / `$between`).
2218

23-
**The remedy.** Write `true` or `false`, or in a querystring `true` / `false` or `1` / `0`.
19+
**What moves for consumers.** Nothing in this package refuses or narrows a filter, and every existing export is unchanged. The door that applies the verdict ships in the same release in `@objectstack/objectql`, whose changeset states what changes for a caller.

0 commit comments

Comments
 (0)