Skip to content

Commit 47e6601

Browse files
os-muskclaude
andauthored
test(drivers): collapse ContainsAny's distributivity so union-shaped doors assert (#18029)
Fixes #17970 Clause-②: no ⚠️ Generic angle brackets are spelled with SQUARE brackets throughout this body. The GitHub body sanitizer eats tag-shaped fragments, backticks and fences included, so `ContainsAny[T]` below means the ordinary generic spelling. `ContainsAny` — the type-level `any` detector #17690 installed in these two door-pinning test files — distributes over unions. Every union-shaped door therefore evaluated to `boolean` once regressed, and because each leg is spelled `const x: ContainsAny[Door] = false` and `false` is assignable to `boolean`, the leg compiled straight through the regression it exists to name. The phantom leg the detector was installed to close was still open on the detector itself. ⛔ This does not make #17690 wrong. Its nested-`any` reading is genuinely effective on NON-union doors — `Record[string, any]` answers `true` under both the old and the new spelling, and that is the control which proves it. Union distribution was a SECOND blind spot standing beside it, never a correction of it. ## What changed Exactly two files, both `.test.ts`: - `packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts` - `packages/drivers/driver-turso/src/turso-driver-doors-declared-types.test.ts` Option **B** (central), per the dispatch order's Zone 1. The per-member walk is unchanged and keeps its own name, `ContainsAnyPerMember[T]`; `ContainsAny[T]` is now its collapse — `ContainsAnyPerMember[T] extends false ? false : true` — so it answers `false` only when EVERY union member answered `false`. `IsAny` is untouched. No export, no published payload key and no acceptance set is touched, which is why Clause-② stays `no`. The detector's own docblock is rewritten in the same change. Before this, its closing sentence read: "The branch order matters: `IsAny[T]` is asked FIRST so a bare `any` never reaches a distributive conditional, where it would split across both arms and answer `boolean`." That sentence's scope changes once the union case is fixed — the branch order is now one of TWO guards, not the guard — so leaving it would have left a half-true statement behind the fix. It now names both guards, what each one does NOT cover, and the measurement. ## Measurements Every line below is a measurement unless marked 【expectation】. There are none marked 【expectation】. ### 1. Detector semantics — standalone `tsc --strict` program, 29 asserted equalities Instrument: a file with no repo imports carrying the old detector text verbatim from `origin/main` `2f1a6f6968` and the new one side by side; each claim is an `Eq[X, Y] = true` const, so tsc reds any claim that does not hold. - probe :: **exit 0** — all 29 equalities hold. - FIRING CONTROL, three deliberately-false claims in the same instrument :: **exit 2, 3 errors** — so the green above is a reading, not an inert file. | shape | old detector | new detector | |---|---|---| | `Record[string, unknown]` | `false` | `false` | | `Record[string, unknown][]` | `false` | `false` | | `unknown` | `false` | `false` | | `Record[string, unknown] | null` | `false` | `false` | | `Record[string, unknown] | undefined` | `false` | `false` | | `{ k?: boolean } | undefined` | `false` | `false` | | `any` | `true` | `true` | | `any[]` | `true` | `true` | | `Record[string, any]` | `true` | `true` | | `Record[string, any][]` | `true` | `true` | | `Record[string, any] | null` | ⛔ `boolean` | ✅ `true` | | `Record[string, any] | undefined` | ⛔ `boolean` | ✅ `true` | | `{ k?: any } | undefined` | ⛔ `boolean` | ✅ `true` | The three `boolean` rows are the defect, and the probe also compiles `const leg: OldDetector[RegressedUnionDoor] = false` for each of them, which is what "the leg stays green" means in bytes. ⚠️ One correction, recorded rather than quietly re-run: my first probe claimed the NON-regressed optional-options door answered `boolean` under the old detector. tsc refused that claim. It answers `false` — a union of clean members is `false` under both detectors, and the blindness appears only when one member carries `any` and another does not. The claim was corrected and the probe re-run. ### 2. No false positives — the real doors `pnpm --filter @objectstack/driver-sql --filter @objectstack/driver-turso typecheck` :: **exit 0** after the change. That covers all 19 `ContainsAny[Door] = false` legs plus everything else in both programs, so Zone 1.1's switch-to-A condition did NOT trigger. Both test files are proven to be IN their package's tsc program (`tsc --noEmit --listFiles`, 1 hit each, 600 / 450 files listed) — the `typecheck` script is not excluding them. ### 3. The regression now reds — ablation, four runs, each with its restore proven Each run: mutate the driver's own `findOne` return annotation to `Promise[Record[string, any] | null]`, prove the mutation landed on disk (`grep -c` of the deleted anchor AND of the injected text, before and after), run the package's `tsc --noEmit`, restore via `git checkout HEAD -- path`, then prove the restore by `git diff HEAD` being empty AND all four blobs hashing equal to HEAD. `trap ... EXIT INT TERM` with absolute paths. tsc reads the driver SOURCE here, not `dist` — `--listFiles` shows `src/sql-driver.ts` / `src/turso-driver.ts` in the respective programs — so no rebuild sits between the mutation and the reading. | run | tree | package | errors | which legs | |---|---|---|---|---| | 1 | **after** this fix | driver-sql | **2** | `sqlFindOneHasAny` (the `ContainsAny` leg) AND `sqlFindOneIsContract` (the `Equals` leg) | | 2 | CONTROL: `origin/main` detector | driver-sql | **1** | `sqlFindOneIsContract` only — `sqlFindOneHasAny` compiled through it | | 3 | **after** this fix | driver-turso | **2** | `tursoFindOneHasAny` AND `tursoFindOneIsContract` | | 4 | CONTROL: `origin/main` detector | driver-turso | **1** | `tursoFindOneIsContract` only — `tursoFindOneHasAny` compiled through it | Runs 2 and 4 reproduce the card's own reading independently. Runs 1 and 3 are the answer to the dispatch order's 【expectation】 about #17690's `findOne` leg: it is a real assertion now, measured rather than cited, in both files. ### 4. Suites `pnpm --filter @objectstack/driver-sql --filter @objectstack/driver-turso test` :: **exit 0** — driver-sql 2620 passed / 166 skipped, driver-turso 1248 passed. ### 5. Repo gates `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived **52** families for these two paths. All 52 were run with `$?` captured BEFORE any pipe, and the record was reconciled back with `--ran`: > 52 derived famil(ies) accounted for — 49 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3). 49 :: exit 0. Three :: **exit 3, PREREQUISITE NOT MET, so NOT MEASURED, ⛔ not a pass and not a failure** — `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:type-check-debt`. All three refuse because they load BUILT entry points across the whole repo and only this card's two packages are built locally. CI builds everything, so CI measures them. ## Changeset grading — `skip-changeset` The criterion is whether the changed TEXT reaches `dist`, ⛔ not whether any `dist` byte moves. Measured after rebuilding both packages from the post-change source, grepping only the paths `files[]` actually ships (`dist`, `README.md`, `CHANGELOG.md`): | symbol | driver-sql `dist` | driver-turso `dist` | |---|---|---| | `ContainsAnyPerMember` (added text) | 0 files | 0 files | | `ContainsAny` (changed text) | 0 files | 0 files | | "never reaches the distributive arms" (added docblock) | 0 files | 0 files | | POSITIVE CONTROL `SqlDriver` / `TursoDriver` | 4 files | 4 files | | POSITIVE CONTROL `temporalFilterValue` | 4 files | — | Zero hits for the changed text against a firing positive control, and zero `.test.*` files emitted into either `dist` at all. One honest complication, checked rather than assumed: the literal string `doors-declared-types` DOES appear in both dists — it comes from TSDoc in `sql-driver.ts` / `turso-driver.ts` naming the pin file, which this diff does not touch. ⇒ nothing published moves. `skip-changeset`, applied as a label. ## Acceptance notes - **The `ContainsAny` detector exists in exactly these two files.** `git grep 'type ContainsAny'` returns 4 lines, all in this diff. There is no third copy to keep in sync, and no shared definition was touched. - **11 further test files still carry the bare `IsAny` template**, which is the phantom check #17690 replaced here — including the two `driver-memory` files frozen by #5499 (`memory-find-create-declared-types.test.ts`, `memory-update-declared-null.test.ts`) and `turso-driver-options-door.test.ts`. Noted, NOT filed: this is #17879's surveyed territory and the freeze lane's for the two `driver-memory` files, both of which have a named owner. Triage on this card already ruled the #5499 exception channel does not open here, so those files are deliberately untouched and the net benefit of this change to the frozen family is zero — which does not change its value on the doors outside the freeze. Session: https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM --- _Generated by [Claude Code](https://claude.ai/code/session_01RuoNSXUbBoWHkNS4AknTrM)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4daa69d commit 47e6601

2 files changed

Lines changed: 92 additions & 10 deletions

File tree

packages/drivers/driver-sql/src/sql-driver-doors-declared-types.test.ts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,59 @@ type Resolved<F> = F extends (...args: never[]) => PromiseLike<infer R> ? R : ne
129129
* and the CELL of a record row — so `any[]`, `Record<string, any>` and
130130
* `Record<string, any>[]` all answer `true` while the contract's own
131131
* `Record<string, unknown>[]` / `Record<string, unknown>` / `unknown` answer
132-
* `false`. The branch order matters: `IsAny<T>` is asked FIRST so a bare `any`
133-
* never reaches a distributive conditional, where it would split across both
134-
* arms and answer `boolean`.
132+
* `false`.
133+
*
134+
* [#17970] Answering `boolean` is the ONE failure mode this detector has to
135+
* stay out of, and staying out of it takes TWO guards, because `T` can reach a
136+
* distributive conditional for two unrelated reasons. Every leg below is
137+
* spelled `const x: ContainsAny<Door> = false`, and `false` is ASSIGNABLE to
138+
* `boolean` — so a door whose detector answers `boolean` has an INERT leg: it
139+
* compiles, it is green, and it is green against the very regression it exists
140+
* to name. That is this file's own lesson for the third time, now about the
141+
* instrument the second round installed.
142+
*
143+
* The branch order is the FIRST guard: `IsAny<T>` is asked before anything
144+
* else, so a bare `any` never reaches the distributive arms of
145+
* `ContainsAnyPerMember`, where it would split across both and answer
146+
* `boolean`. That guard is about `T` BEING `any`, and it does nothing when `T`
147+
* is a UNION — which `findOne`'s `Record<string, unknown> | null` is, along
148+
* with every not-found and optional-argument door in this family. A union
149+
* distributes member by member, so a regression to `Record<string, any> | null`
150+
* answers `true` for the record member and `false` for the `null` member:
151+
* `boolean` again, reached by a different route, and the `findOne` leg below
152+
* sat green through exactly that.
153+
*
154+
* `ContainsAny` is therefore the COLLAPSE of the per-member answer — `false`
155+
* only when EVERY member answered `false` — which is the SECOND guard.
156+
* Measured in a standalone `tsc --strict` program, with deliberately-false
157+
* claims as the firing control: per-member alone, `Record<string, any> | null`,
158+
* `Record<string, any> | undefined` and `{ k?: any } | undefined` each answered
159+
* `boolean`; collapsed, all three answer `true`, while
160+
* `Record<string, unknown> | null`, `Record<string, unknown> | undefined`,
161+
* `Record<string, unknown>`, `Record<string, unknown>[]` and `unknown` all
162+
* still answer `false`.
163+
*
164+
* ⛔ None of this makes the nested-`any` reading above wrong. On a NON-union
165+
* door it was effective and remains effective — `Record<string, any>` answers
166+
* `true` under both spellings, which is the control that proves it. Union
167+
* distribution was a SECOND blind spot standing beside it, never a correction
168+
* of it.
135169
*/
136-
type ContainsAny<T> = IsAny<T> extends true
170+
type ContainsAnyPerMember<T> = IsAny<T> extends true
137171
? true
138172
: T extends readonly (infer Row)[]
139-
? ContainsAny<Row>
173+
? ContainsAnyPerMember<Row>
140174
: T extends Record<string, infer Cell>
141175
? IsAny<Cell>
142176
: false;
143177

178+
/**
179+
* [#17970] The collapse. `ContainsAnyPerMember<T>` is distributive, so on a
180+
* union door it answers a UNION of per-member verdicts; this reports `false`
181+
* only when that union is exactly `false`, turning any `boolean` into `true`.
182+
*/
183+
type ContainsAny<T> = ContainsAnyPerMember<T> extends false ? false : true;
184+
144185

145186
// `explain` is optional on the contract (`explain?(...)`), so its function type
146187
// is read through `NonNullable` — the door is the member, not its presence.

packages/drivers/driver-turso/src/turso-driver-doors-declared-types.test.ts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,18 +129,59 @@ type Resolved<F> = F extends (...args: never[]) => PromiseLike<infer R> ? R : ne
129129
* and the CELL of a record row — so `any[]`, `Record<string, any>` and
130130
* `Record<string, any>[]` all answer `true` while the contract's own
131131
* `Record<string, unknown>[]` / `Record<string, unknown>` / `unknown` answer
132-
* `false`. The branch order matters: `IsAny<T>` is asked FIRST so a bare `any`
133-
* never reaches a distributive conditional, where it would split across both
134-
* arms and answer `boolean`.
132+
* `false`.
133+
*
134+
* [#17970] Answering `boolean` is the ONE failure mode this detector has to
135+
* stay out of, and staying out of it takes TWO guards, because `T` can reach a
136+
* distributive conditional for two unrelated reasons. Every leg below is
137+
* spelled `const x: ContainsAny<Door> = false`, and `false` is ASSIGNABLE to
138+
* `boolean` — so a door whose detector answers `boolean` has an INERT leg: it
139+
* compiles, it is green, and it is green against the very regression it exists
140+
* to name. That is this file's own lesson for the third time, now about the
141+
* instrument the second round installed.
142+
*
143+
* The branch order is the FIRST guard: `IsAny<T>` is asked before anything
144+
* else, so a bare `any` never reaches the distributive arms of
145+
* `ContainsAnyPerMember`, where it would split across both and answer
146+
* `boolean`. That guard is about `T` BEING `any`, and it does nothing when `T`
147+
* is a UNION — which `findOne`'s `Record<string, unknown> | null` is, along
148+
* with every not-found and optional-argument door in this family. A union
149+
* distributes member by member, so a regression to `Record<string, any> | null`
150+
* answers `true` for the record member and `false` for the `null` member:
151+
* `boolean` again, reached by a different route, and the `findOne` leg below
152+
* sat green through exactly that.
153+
*
154+
* `ContainsAny` is therefore the COLLAPSE of the per-member answer — `false`
155+
* only when EVERY member answered `false` — which is the SECOND guard.
156+
* Measured in a standalone `tsc --strict` program, with deliberately-false
157+
* claims as the firing control: per-member alone, `Record<string, any> | null`,
158+
* `Record<string, any> | undefined` and `{ k?: any } | undefined` each answered
159+
* `boolean`; collapsed, all three answer `true`, while
160+
* `Record<string, unknown> | null`, `Record<string, unknown> | undefined`,
161+
* `Record<string, unknown>`, `Record<string, unknown>[]` and `unknown` all
162+
* still answer `false`.
163+
*
164+
* ⛔ None of this makes the nested-`any` reading above wrong. On a NON-union
165+
* door it was effective and remains effective — `Record<string, any>` answers
166+
* `true` under both spellings, which is the control that proves it. Union
167+
* distribution was a SECOND blind spot standing beside it, never a correction
168+
* of it.
135169
*/
136-
type ContainsAny<T> = IsAny<T> extends true
170+
type ContainsAnyPerMember<T> = IsAny<T> extends true
137171
? true
138172
: T extends readonly (infer Row)[]
139-
? ContainsAny<Row>
173+
? ContainsAnyPerMember<Row>
140174
: T extends Record<string, infer Cell>
141175
? IsAny<Cell>
142176
: false;
143177

178+
/**
179+
* [#17970] The collapse. `ContainsAnyPerMember<T>` is distributive, so on a
180+
* union door it answers a UNION of per-member verdicts; this reports `false`
181+
* only when that union is exactly `false`, turning any `boolean` into `true`.
182+
*/
183+
type ContainsAny<T> = ContainsAnyPerMember<T> extends false ? false : true;
184+
144185

145186
type ContractFindOne = Resolved<IDataDriver['findOne']>;
146187
type ContractCreate = Resolved<IDataDriver['create']>;

0 commit comments

Comments
 (0)