Repository navigation
Commit cf0346e
Fixes #20821
Clause-②: no
`os migrate plan` against a database that does not exist yet printed six
`[sql-driver] DATABASE_ERROR … no such table` lines on stderr. The plan
boots with the SQL driver's DDL deferred, lists every table as pending
`create_table`, and then the same boot reads `sys_metadata`,
`sys_metadata_activation` and `sys_migration`. Every one of those reads
was refused, and every reader already answered from the refusal. Only
the driver's warn line was wrong.
This follows the seat answer on the card (5924276655): Q1 **A**, a
driver-side demotion keyed on the driver's own deferral; Q2 **A**, the
pin is scoped to the three deferred tables.
## The six readers (traced with a stack per line at `b253fadfb`;
unchanged at this head)
| table | reader | when |
|:--|:--|:--|
| `sys_metadata` | `ObjectQLPlugin.restoreMetadataFromDb` →
`ObjectStackProtocolImplementation.loadMetaFromDb`
(`isMissingTableError` → `loaded: 0`) | `ObjectQLPlugin.start()`, phase
2 |
| `sys_metadata` | `readAuthoredTranslationLayer` (`core`,
`fallbacks/authored-translation-sync.ts`) → `null` | `kernel:ready` |
| `sys_metadata` | `ObjectQLPlugin.readAuthoredHookRows` via
`resyncAuthoredHooksNow` → `null` | `kernel:ready` |
| `sys_metadata` | `ObjectQLPlugin.readAuthoredActionRows` via
`resyncAuthoredActionsNow` → `null` | `kernel:ready` |
| `sys_metadata_activation` | `ObjectStoreActionActivationStore.probe`
via `ObjectQLPlugin.hydrateActionActivations` → its own functional warn
| `kernel:ready` |
| `sys_migration` | `ObjectQL.announceOpenMigrationGates` →
`readMigrationFlagVerified` → "not verified, not conclusive" |
`kernel:bootstrapped` |
The `DATABASE_ERROR` line is written inside
`SqlDriver.backendStatementFault`, before any of these readers sees the
error. So a reader-side catch cannot remove it. "Not asked" was measured
and declined: no reader can see the deferral, because no `IDataDriver`
member and no kernel key carries it. Deferred is also not the same as
absent: on an existing database the deferral records every object before
any `hasTable`. Making "not asked" honest would need a new contract
fact, and `Clause-②` would become yes.
## The change
`packages/drivers/driver-sql/src/sql-driver.ts`,
`SqlDriver.backendStatementFault`, in the warn decision only. A refused
statement goes to `logger.debug` instead of `logger.warn` only when all
three conditions hold:
1. this driver has DDL deferred (`deferredDdl`);
2. the targeted table is in this driver's own `deferredSchemaObjects`;
3. `isMissingTableError(envelope, object)` holds. This is the one shared
predicate, asked over the envelope's declared target. No second message
regex.
The throw and the envelope (`DATABASE_ERROR`, status 500) are unchanged.
Every other refusal still warns: a malformed statement on a table that
exists, a missing table the driver did not defer, and anything after
`flushDeferredSchemaDdl` (which clears both the flag and the set). The
new branch sits beside PR #20818's pre-DDL scope check. It is disjoint
from the PR #20988 hunks, the JSON-column gate region and the
JSON-membership helpers.
### Kept on purpose
- **The stdout WARN from `ObjectQLPlugin.hydrateActionActivations`**
(`sys_metadata_activation is registered but could not be read —
packaged-ACTION enable/disable is UNAVAILABLE on this deployment …`) is
kept. It is the reader's own functional line, not the driver's, and the
stdout-identity pin requires it. On a dry run against an absent file it
is a false alarm, and that is recorded here.
- **The `examples/app-crm` app-hook lines** (`sys_position` ×3,
`sys_permission_set` ×3) are kept. They come from the app's `onEnable`
`kernel:bootstrapped` hook, which reads tables the plan's composition
never declares. That is a different door: #21054 is not addressed here,
and the pin does not count those lines.
## Before and after on `examples/app-crm`
`node ../../packages/cli/bin/run.js migrate plan [--json] --database-url
file:ABSENT.sqlite`. The base is `576afc17b` (before the fix). The head
is `ea9309b80`, rebuilt.
| reading | base | head |
|:--|:--|:--|
| `DATABASE_ERROR` lines on stderr | 12 | 6 |
| … naming `sys_metadata` / `sys_metadata_activation` / `sys_migration`
| 6 (4 / 1 / 1) | **0** |
| stderr diff | — | exactly those 6 lines removed, 0 added |
| human stdout, normalised for timestamps and `Nms` durations | — |
`diff` exit 0 |
| the plan block (`ℹ Database:` … `Apply with:`) md5 |
`e102064c7b13bce96303c1e9b698693d` | `e102064c7b13bce96303c1e9b698693d`
|
| `--json` stdout with `duration` removed | — | byte-identical (`cmp`
exit 0) |
| database file left behind | none | none |
## Tests
- **Driver pin, new:**
`packages/drivers/driver-sql/src/sql-driver-20821-deferred-ddl-missing-table.test.ts`,
on a real SQLite file. Every case asserts the envelope `code` and
`status`.
- ① DDL deferred, the table in the deferred set and missing: the refusal
goes to `debug` (with `no such table`), and `warn` stays empty. After
`flushDeferredSchemaDdl` the same read answers `[]` with nothing logged.
- ② Control: a malformed read (40,000 bound variables) on an existing
table whose DDL is deferred still warns.
- ③ Control: a missing table outside the deferred set, on the same
deferred driver, still warns.
- **CLI pin, new:**
`packages/cli/src/commands/migrate/plan.deferred-reads.integration.test.ts`
(integration tier: it spawns `bin/run-dev.js`). Its fixture is one host
config with a lookup field. On the base, that fixture reproduced exactly
the card's 6 lines (4 / 1 / 1). In human mode and under `--json`, it
asserts:
- 0 `DATABASE_ERROR` lines naming the three tables;
- the reads still answered: the open `[value-shape]` gate is announced,
and the activation WARN is present;
- each of the three tables is still pending `create_table`;
- no file is written.
- **Suites at `ea9309b80`:**
- `pnpm --filter @objectstack/driver-sql exec vitest run
--maxWorkers=2`: 204 files passed, 11 skipped; 3285 tests passed, 188
skipped. The skips are the live PG and MySQL cells, which are not
provisioned here.
- `@objectstack/cli` `--project unit`: 240 files, 3419 tests passed.
- `@objectstack/cli` `--project integration`, run on this PR's file plus
`schema-migrate.readonly-probe` and `schema-migrate.deferred-ddl`: 3
files, 9 tests passed.
- **NOT MEASURED: the full cli integration tier.** Reason: it ran past
the 10-minute foreground cap here (exit 124 at 595s). It is declared to
CI.
- `typecheck` for `driver-sql` and `cli` is green, including `cli`'s
`check:test-typecheck` (debt unchanged). The new cli pin is inside
`tsc`'s program, because `src` is the `include`.
## Reverse verification (from the committed fix, through
`scripts/ablation-replace.mjs`)
1. **The deferred-set condition removed** (anchor
`this.deferredSchemaObjects.has(targetedTable) &&`, 1 → 0; blob
`c626b59d` → `fecb8704`). The driver pin turned **red on ③ only**
(`expected [] to have a length of 1 but got +0`), and ① and ② stayed
green. The driver pin imports `src`, so no build was involved. Restore:
blob `c626b59d` equals HEAD, and `git diff HEAD` is empty.
2. **The debug branch removed** (the whole `if` block, 1 → 0; blob
`c626b59d` → `e7acc7e7`). `driver-sql` was rebuilt.
`ablation-dist-preflight --absent 'this driver deferred its DDL'` found
the marker absent from all 6 built files. The CLI pin turned **red on
both cases** (`expected [ …(6) ] to deeply equal []`). Restore: blob
equals HEAD and `git diff HEAD` is empty. After a rebuild, the preflight
found the marker present in 2 built files and the tree clean against
HEAD, and both pins were green again. This leg was run again at
`fd9f151a4`, after the pin's spawn harness changed: same result.
## Gates
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `ea9309b80` derived 66 families. All 66
were run, with exit codes recorded before any pipe, and all exited 0.
`--ran` reports: `66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN`.
- `check:dual-build-cjs-loads` and `check:i18n-coverage` first answered
`PREREQUISITE NOT MET` (exit 3) on the earlier merge head, because nine
packages had no `dist/`. Those packages were built and both gates re-ran
green.
- The derivation warned that the tree was behind `origin/main` and named
two of its inputs as changed: `lint.yml` gained timeout keys and
comments, and `engine-double-contract.pinned.json` gained entries for
other files. Neither changes the family set. No commit since the merge
touches `driver-sql` or `cli/src/commands/migrate`.
- `node scripts/check-driver-conformance.mjs`, before and after: `OK —
50 covered cell(s), 0 in the DEBT ledger, 0 exempt.`
- Lint, a proven narrowing: `eslint --no-inline-config --format json`
over the 3 touched TypeScript files reports 3 files, 0 errors, 0
warnings. `eslint --print-config` resolves each of them, and none
carries `parserOptions.project`. `eslint.config.mjs` enables no
type-aware linting, so this diff cannot move the verdict on any file it
does not touch. The repo-wide `pnpm lint` is CI's.
## Changeset
`.changeset/20821-plan-deferred-ddl-reads.md`: a `patch` for
`@objectstack/driver-sql`. `@objectstack/cli` ships only `dist`,
`README.md` and `CHANGELOG.md`, and `tsconfig.build.json` excludes
`src/**/*.test.ts`, so the pin publishes nothing and gets no entry.
## Acceptance notes
- In this worktree, one gate run wrote
`examples/app-crm/dist/objectstack.json` (gitignored). With that
compiled artifact present, `os migrate plan` takes the artifact boot
path: `onEnable` does not run, and the six #21054 lines disappear too.
The after-measurement above was taken with that artifact removed, so it
matches the base's no-artifact path. This is noted for whoever takes
#21054, since whether the app hook runs depends on that file.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Ujdtvqs7ree7WyQmEDwEnG)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5e470f8 commit cf0346e
4 files changed
Lines changed: 418 additions & 0 deletions
File tree
- .changeset
- packages
- cli/src/commands/migrate
- drivers/driver-sql/src
| 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 | + | |
Lines changed: 184 additions & 0 deletions
| 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 | + | |
Lines changed: 176 additions & 0 deletions
| 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 | + | |
0 commit comments