Repository navigation
Commit 4dde0ae
fix(spec)!: SqliteConfigSchema.filename refuses '' and a ':'-prefixed name other than ':memory:' at the build and metadata doors (#30605)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuV5cRafodM2YQHxeDkdiWJM)_
domain:spec#2 · os-sales · session_01L6bhgUkEKsyGSzebnh2wdV (card thread
of claim 6663609014)
Fixes #30400
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — `skills/**` teaches no SQLite `filename` value, and
`content/docs/**` teaches only `:memory:` and file paths, which stay
accepted. The prescription ships in the refusal text, the changeset's
FROM → TO and the upgrade-guide entry.
## 维护者速读
- 改了什么:SQLite / SQLite-WASM 数据源的 `filename` 现在在 spec 里就拒收空串和「以 `:` 开头但不是
`:memory:`」的值(例如 `:memory:/srv/data/parts.db`),代码声明的数据源(`os build` /
`defineStack`)和 `PUT /api/v1/meta/datasource/NAME` 都会报错并给出改法。
- 为什么改:这类值哪里都打不开真实文件:原生 sqlite 每条语句失败,sqlite-wasm
写入在重连后全部丢失;之前只有数据源管理界面拦,另外两个入口照样存。
- 风险与回滚:已存在这种配置的代码数据源会在构建时报错(本来就不能用);回滚即还原 `sqlite.zod.ts` 的 refinement
与服务端文件,无数据迁移。
-
先看:`packages/spec/src/data/driver/sqlite.zod.ts`(`sqliteFilenameRefusal`)、`packages/services/service-datasource/src/sqlite-database-filename.ts`(删掉的两段改由
spec
判)、`packages/spec/src/data/driver/sqlite-filename-names-a-database.test.ts`。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/data/driver/sqlite-filename-names-a-database.test.ts`,预期 44 个用例全绿。
- 需要你做的:无,席位落地。
## What changes
- **`@objectstack/spec`** — `SqliteConfigSchema.filename` and
`SqliteWasmConfigSchema.filename` share one builder, `sqliteFilename()`,
which adds a refinement after the existing placeholder refusal: `''` and
any value that starts with `:` without being exactly `:memory:` get one
`custom` issue at `filename` that names the value, says why (a leading
`:` is not read as a file, so the datasource would store nothing) and
prescribes the fix (`:memory:` for an ephemeral database, or a file
path). The default stays `:memory:`; `.describe()` / `.meta()` are
unchanged, so no generated artifact moved. The filesystem judgement (a
path that can be neither opened nor created) stays in the runtime: the
schema cannot touch the file system.
- **Both doors that judge a datasource against the spec alone now refuse
it**: the build door (`ObjectStackDefinitionSchema`, which `os build`
parses in `packages/cli/src/commands/compile.ts` and `defineStack`
raises as `STACK_SCHEMA_INVALID`), and the metadata door (`PUT
/api/v1/meta/datasource/NAME`, which judges with the registered
`datasource` type schema, `DatasourceSchema`, whose config refinement
replays the driver contract and answers `422 INVALID_METADATA`).
- **One definition** — `@objectstack/service-datasource`'s
`sqlite-database-filename.ts` loses its own `''` and `:`-prefix arms.
`assertValidConfig` already runs the driver-contract parse
(`validateDriverConfig`) first and calls the filename judge only when
that is clean, so the admin doors now get those two refusals from the
spec, word for word as before (`config.filename:` + the same sentence).
Its filesystem arm is kept. The function's docblock states the
precondition and the ⛔ against restating the arms.
- **ADR-0087** — semantic migration
`sqlite-filename-names-no-database-refused` (step 18): no mechanical
rewrite, because whether the author meant `:memory:` or the path typed
after it is a judgement about the datasource.
- **Changeset** `30400-sqlite-filename-names-a-database.md`:
`@objectstack/spec` minor, `@objectstack/service-datasource` minor,
BREAKING banner, FROM → TO, disposition `registered
sqlite-filename-names-no-database-refused`.
## Verification
Readings on `objectstack-ai/objectstack`. Final head `5c12bcd4`
(origin/main merged in): spec `check:generated` 14/14, the new pins
44/44 and `service-datasource` 72/72 files (1195 tests) were re-run
there. Steps naming `9e360bb3` were taken on the pre-merge head; the
derived-gate re-sweep on the final head is in the os-dev-report.
<details><summary>New pins — 44/44</summary>
`pnpm --filter @objectstack/spec exec vitest run
src/data/driver/sqlite-filename-names-a-database.test.ts` → `Tests 44
passed (44)`.
Per schema (sqlite, sqlite-wasm): the registry answers this schema for
the driver id; six refused values (`:memory:/srv/data/parts.db`,
`:memory:parts.db`, `:mem`, `::memory:`, `:`, `''`), each one issue,
`code: 'custom'`, `path: ['filename']`, message opening with the value
and carrying the prescription, no tracker number; the why sentence;
composition with the placeholder refusal (`:${DATA_DIR}/app.db` reports
both); the alias table (`path:` is still `unrecognized_keys` pointing at
`filename`, and the renamed value meets the refusal); controls
`:memory:`, `data/app.db`, `./.objectstack/data/dev.db`,
`/var/lib/objectstack/app.db`, `C:\data\app.db`, `file:app.db`,
`memory.db` parse unchanged and the absent key still defaults to
`:memory:`. Doors: `getMetadataTypeSchema('datasource')` is
`DatasourceSchema` and refuses at `config.filename`;
`ObjectStackDefinitionSchema` refuses at
`datasources.0.config.filename`; `defineStack` raises
`STACK_SCHEMA_INVALID` / 422 there; both doors still accept `:memory:`
and a file path.
The metadata door's HTTP envelope for a replayed driver-config refusal
(`422 INVALID_METADATA` located under `config.`) is pinned live by the
existing
`packages/qa/dogfood/test/datasource-meta-door-url-credential-trimmed.dogfood.test.ts`
(same `DatasourceSchema` → `validateDriverConfig` path, at
`config.url`).
</details>
<details><summary>Ablation 1 — spec source (pre-merge head
9e360bb)</summary>
`node scripts/ablation-replace.mjs --file
packages/spec/src/data/driver/sqlite.zod.ts --anchor "if (refusal)
ctx.addIssue({ code: 'custom', message: refusal });" --replacement "void
refusal; void ctx; …"`: anchor 1 → 0, blob 33624c6 → 0a5f9556. Pin
file: `Tests 24 failed | 20 passed (44)` — every refusal, why,
composition, renamed-alias and door case red; every control green.
Restored: blob == HEAD, `git diff HEAD` empty.
</details>
<details><summary>Ablation 2 — spec dist, read by the service door test
(one definition)</summary>
Same anchor, replacement carrying `(globalThis as any).ABLATED_30400 =
true;`; `OS_SKIP_DTS=1 pnpm --filter @objectstack/spec build`;
`ablation-dist-preflight.mjs @objectstack/spec ABLATED_30400` → marker
present in 2 built files. `service-datasource`
`sqlite-filename-neither-memory-nor-path.test.ts` → exactly the six
`:memory:/…` and empty-filename cases red (Test connection and Save,
both drivers); the filesystem-arm cases and controls green — the service
holds no second copy. Restore leg: blob == HEAD, full spec build,
preflight `--absent` → marker absent from all 592 built files, tree
clean; the file is 14/14 again.
</details>
<details><summary>Suites</summary>
- `@objectstack/spec` test (pre-merge head): `Test Files 816 passed
(816)`, `Tests 25687 passed | 1 todo`; typecheck exit 0;
`check:test-typecheck` OK (52 files / 240 errors / 131 pinned signatures
held).
- `@objectstack/service-datasource` test: `Test Files 72 passed (72)`,
`Tests 1191 passed (1191)`; typecheck exit 0.
- Consumers that parse a SQLite config (pre-merge head):
`@objectstack/driver-sql` 238 passed / 12 skipped files (3859 tests
passed); `@objectstack/driver-sqlite-wasm` 40/40 (750);
`@objectstack/runtime` 544/544 (8658 passed, 19 skipped). Fixture sweep
(`git grep` for `filename` set to `''` or a `:`-prefixed value other
than `:memory:` outside dist): no SQLite fixture writes one; the only
hits are the #30331 door test (which asserts the refusal) and unrelated
`filename` keys (email attachments, print, storage).
- `@objectstack/spec check:generated` (after a spec build): 14/14 up to
date (the first run's `check:test-typecheck` was a heap exit without
diagnostics; re-run with an 8 GB heap, OK).
- eslint (`--no-inline-config`) on the four changed source files: exit
0.
- `node scripts/check-adr-0087-registration.mjs --base origin/main`:
exit 0, `registered sqlite-filename-names-no-database-refused (new
here)`.
</details>
<details><summary>Derived gates — 84 derived, 84 run, 0
NOT-MEASURED</summary>
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at 9e360bb: 84 families. 81 exit 0 on the
first sweep; `check:api-surface` and `check:dual-source-exports`
answered exit 3 (spec dist removed under them by a parallel consumer
build) and `check:dual-build-cjs-loads` exit 3 (no full build yet) —
prerequisites, not verdicts; after a full `turbo run build` all three
exit 0. `--ran` → `84 derived famil(ies) accounted for — 84 run, 0
NOT-MEASURED (a DERIVED zero)`.
</details>
Dogfood battery: run on the final head; its counts are in the
os-dev-report on #30400.
## Acceptance notes
- **The JSON Schema projection does not carry the refusal.** A
`superRefine` does not project, so the driver catalog's `configSchema`
(`GET /api/v1/datasources/drivers`) still types `filename` as a plain
string. A `pattern` would let a JSON-Schema form refuse inline, but it
cannot name the value and it changes what the Setup dialog validates
(`ui/**`, `domain:ui#5`); left out on purpose.
- **A producer that can emit a now-refused value**:
`sqliteFilenameFromUrl` (`packages/runtime/src/standalone-stack.ts`)
turns `OS_DATABASE_URL=file::memory:?cache=shared` into
`:memory:?cache=shared`. That value already opened no file (every layer
reads the leading `:` as fileless); it is now refused wherever it is
judged against the spec. Not changed here.
- `.changeset/30331-sqlite-filename-neither-memory-nor-path.md`
(unreleased) says the build and meta doors judge `filename` "as before";
true when written, superseded by this changeset in the same release. Not
edited (another card's changeset).
- Branch is the harness-assigned `claude/project-thread-eau1kb`, not
`claude/issue-30400-*`; the claim amendment names it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgUkEKsyGSzebnh2wdV
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent dc51811 commit 4dde0ae
5 files changed
Lines changed: 332 additions & 18 deletions
File tree
- .changeset
- packages
- services/service-datasource/src
- spec/src
- data/driver
- 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| |||
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
46 | 57 | | |
47 | 58 | | |
48 | 59 | | |
49 | 60 | | |
50 | 61 | | |
51 | 62 | | |
52 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
53 | 68 | | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | 69 | | |
65 | 70 | | |
66 | 71 | | |
| |||
| 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 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
0 commit comments