Repository navigation
Commit 1a4c7f8
fix(spec): filter-text-operator-declared-type-refused's control excludes JSON-stored fields and names the JSON-column door (#21234)
Fixes #21189
Clause-②: no
## What changes
Text only, in one `@objectstack/spec` semantic migration entry: the
`acceptanceCriteria` of `filter-text-operator-declared-type-refused`
(protocol 18), plus its regenerated region of
`packages/spec/src/migrations/registry.ts` and one `patch` changeset.
Neither door's code moves. No entry id, `surface`, `replacement`,
`reason`, conversion or refusal changes either.
The old criteria named every text-valued field as the control that "must
keep answering exactly as before". That list included `multiselect` /
`checkboxes` / `tags` and lookup and `user` ids. The declared-type door
this entry registers does still leave all of those alone. A second door
judges a field by how it is **stored**, though, and it now refuses the
prefix, suffix, case-folded and pattern operators on a JSON-stored
column. So the old sentence named filters that answer `400` after the
upgrade as the repair pass's control.
The criteria now say:
- **The control:** a text-valued field that is NOT stored as a JSON
column.
- **The JSON-stored population:** `multiselect` / `checkboxes` / `tags`;
any field declared `multiple: true` (a multi-valued lookup or `user`
among them); and, on a SQL deployment still inside the ADR-0104
dual-encoding window, a single-value file-class field.
- **That door's refusal:** every text operator except the membership
pair `$contains` / `$notContains`, with `INVALID_FILTER` / `400`. Its
message names no declared type, so these refusals are outside this
entry's repair list.
- **The repair:** on a multi-valued field it is membership (`$contains`
for one member, an `$or` of `$contains` for any-of), and no rewrite
chooses it. A single-value file-class field answers text operators again
once the deployment finishes the media-column move (the column step of
`objectstack migrate files-to-references --apply`).
## What the text was checked against (`origin/main` at `99692286`, the
JSON-column door as commit `2c1cef334` left it)
- **The refused set.** `JSON_COLUMN_INCOMPATIBLE_OPERATORS` in
`packages/core/src/utils/json-column-operator-refusal.ts` holds three
groups. The equality family is `$eq` and `$ne` with their bare infix
spellings. The scalar comparisons are the orderings, `$in` / `$nin`
(plus the bare infix spellings) and `$between`. Since `2c1cef334` it
also holds `$startsWith`, `$endsWith`, `$icontains`, `$like` and
`$ilike`. `$contains` / `$notContains` / `$exists` / `$null` / `$empty`
are absent.
- **The refusal.** It is `INVALID_FILTER` / `400` on every face that
reads the set. `driver-sql` raises it through `unsupportedFilterError`,
and the objectql per-aggregation `filter` through `invalidFilterError`.
`driver-memory`'s filter gate reads the same set too.
- **The population.** The engine and `driver-memory` take
`STRUCTURED_JSON_TYPES` together with `isMultiValueField`.
`isMultiValueField` covers `MULTI_OPTION_TYPES` (`multiselect`,
`checkboxes`, `tags`) and any `MULTI_CAPABLE_TYPES` member (`select`,
`radio`, `lookup`, `user`, `file`, `image`) declared `multiple: true`.
`driver-sql`'s `isJsonField` adds the single-value media family
(`FILE_REFERENCE_TYPES`) when `mediaColumnIsJson()` answers true, which
means the media columns have not moved.
**The single-value media arm was measured, not inferred.** A throwaway
SQLite test through `SqlDriver` (deleted, never committed) covered both
arms of the move:
| field | filter | media columns not moved | media columns moved |
|:--|:--|:--|:--|
| `text` | `$startsWith` | rows | rows |
| single `select` | `$startsWith` | rows | rows |
| single `user` | `$startsWith` | rows | rows |
| single `file` | `$startsWith` / `$icontains` | `INVALID_FILTER 400` |
rows |
| `select`, `multiple: true` | `$startsWith` | `INVALID_FILTER 400` |
`INVALID_FILTER 400` |
| `select`, `multiple: true` | `$contains` | membership rows |
membership rows |
The triage direction listed the population as `multiselect` /
`checkboxes` / `tags` and multi-valued lookup / `user`. The entry names
the population the code holds, which is wider: every `multiple: true`
field, and the single-value file classes while the media columns have
not moved. Leaving those out would have left the same untrue control
sentence for them.
## Tests and gates (all at `3a0bcb2d`, after merging `origin/main` at
`62b90d74` through `scripts/pm/os-regen-merge.sh`)
- **Package build and generated artifacts.** `pnpm --filter
@objectstack/spec build`, then `pnpm --filter @objectstack/spec
check:generated`, gave "All 15 generated artifacts are up to date". That
covers `check:migration-registry`, `check:spec-changes` and
`check:upgrade-guide`. The upgrade guide and `spec-changes.json` do not
carry this entry's text, so only `registry.ts` moved.
- **Package tests.** `pnpm --filter @objectstack/spec exec vitest run
--project local --maxWorkers=2` reported 596 files and 17457 tests
passed (1 todo).
- **Typecheck.** `pnpm --filter @objectstack/spec typecheck` exited 0.
`check:test-typecheck` is OK: 52 files, 246 errors, 135 pinned
signatures held.
- **Derived gate families.** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 82 commands from this
tree. Of those, 81 ran with exit 0. `pnpm check:dual-build-cjs-loads` is
NOT MEASURED: it exited 3 (PREREQUISITE NOT MET) because it loads every
package's built output, and that needs a whole-repo build. CI runs it.
The `--ran` reconciliation of the recorded exit codes accounts for all
82 families: 81 run, 1 NOT-MEASURED.
- **Lint, narrowed to the changed files.** The run was `eslint
--no-inline-config --format json` over the two changed `.ts` files. Its
JSON output counts 2 files, 0 errors and 0 warnings. Both files fall
under `eslint.config.mjs`'s `packages/**/*.{ts,tsx,mts,cts}` blocks. The
narrowing is safe because the config never enables type-aware linting
(no `parserOptions.project` and no typed rules, as its own header
states). So this diff cannot change the verdict on any file it does not
touch.
- **The CLI pin that prints this text.**
`packages/cli/test/migrate-meta-engine-guidance.test.ts` spawns the CLI,
so it is the integration tier and is declared to CI. It asserts the
printed block carries no tracker id. A static grep for `#` followed by
4–5 digits over the entry and the changeset found 0 hits. A positive
control of the same regex matched.
## Acceptance notes
- **Not filed: the single-value media arm with `$contains`.** On a SQL
deployment whose media columns have not moved, `$contains` with a
single-value file field's exact id answered zero rows. The JSON-column
door's refusal on that same field prescribes `$contains`. This was
measured at the driver only (the SQLite table above, `doc $contains
'fil_one'`), not through a public door, so it is not filed. Carrier:
none. It is also why the entry gives that field the media-column move as
its repair, not `$contains`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent a7d9768 commit 1a4c7f8
3 files changed
Lines changed: 79 additions & 12 deletions
File tree
- .changeset
- packages/spec/src/migrations
- entries/semantic
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
58 | 60 | | |
59 | 61 | | |
60 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
61 | 78 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11814 | 11814 | | |
11815 | 11815 | | |
11816 | 11816 | | |
11817 | | - | |
11818 | | - | |
11819 | | - | |
11820 | | - | |
11821 | | - | |
| 11817 | + | |
| 11818 | + | |
| 11819 | + | |
| 11820 | + | |
| 11821 | + | |
| 11822 | + | |
| 11823 | + | |
11822 | 11824 | | |
11823 | 11825 | | |
11824 | | - | |
| 11826 | + | |
| 11827 | + | |
| 11828 | + | |
| 11829 | + | |
| 11830 | + | |
| 11831 | + | |
| 11832 | + | |
| 11833 | + | |
| 11834 | + | |
| 11835 | + | |
| 11836 | + | |
| 11837 | + | |
| 11838 | + | |
| 11839 | + | |
| 11840 | + | |
| 11841 | + | |
11825 | 11842 | | |
11826 | 11843 | | |
11827 | 11844 | | |
| |||
0 commit comments