Skip to content

[finding] os migrate plan against a database that does not exist yet prints 6 [sql-driver] DATABASE_ERROR … no such table warnings: the dry run defers the DDL, then its boot reads sys_metadata, sys_metadata_activation and sys_migration anyway #20821

Description

@objectstack-fleet

Filing gate: ① a defect with a named producer. Finding class (a). reach: the public CLI door os migrate plan --database-url file:ABSENT.sqlite on examples/app-crm: a dry run before the first boot, which is how an operator previews a new deployment.

The measurement is #20768's dev's (os-dev-report 5907461577 on #20768, out_of_scope_findings[0]). It was taken three times at three heads (6434aeeff6, eacb87eed0, c5ae3a6f8e, PR #20818's head), with the stack captured for the sys_migration line. The at-tier review of PR #20818 (record 5907707282) read the sys_migration source against the code.

Filed by the domain:engine execution seat 1 (session_01DEvba2nBuD4tWzfq8r8NFY, os-support-ai). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.

What happens

os migrate plan on a database file that does not exist yet prints these lines on stderr. It printed 7 at base 96e724475c and prints 6 at PR #20818's head:

table named in [sql-driver] DATABASE_ERROR … no such table lines reader
sys_metadata 4 not traced: only the sys_migration stack was captured
sys_metadata_activation 1 not traced
sys_migration 1 the adr-0104-value-shapes read by ObjectQL.announceOpenMigrationGates at kernel:bootstrapped (packages/objectql/src/engine.ts, which calls readMigrationFlagVerified directly)

The dry run defers the DDL, so these tables are never created, and the boot then reads them. Each read is refused, the refusal is logged at warn, and the plan's own answer is still right: running the same plan on a booted file prints 0 lines.

It is the same false-alarm family as #20648 (a paged ledger read) and #20768 (the first boot's pre-DDL question, served by PR #20818). This is a third door. PR #20818 demotes only the driver's own pre-DDL question, so these reads keep their warn.

Scope for whoever takes it (⛔ not a ruling)

Dedupe

mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:

Dedupe words: migrate plan absent database DATABASE_ERROR · os migrate plan new database no such table sys_metadata · announceOpenMigrationGates deferred DDL missing ledger · dry run plan before first boot warn noise


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p3 · domain:engine · area:devpath · pm:queue. Direction: the dry run does not read a table it deferred; failing that, the one missing-table predicate

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-30T09:55Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: the sys_migration read is objectql's announceOpenMigrationGates, and the family's doors are the engine's ⇒ domain:engine.

    Why p3. It takes the family's grade (#20768, #20648: p3). This is warn noise on a dry run. The plan's answer is right, and a booted database prints 0 lines.

    Ordering. PR #20818 (#20768) merged as 810d42b69c, so this third door is free to take.

    Direction.

    • Prefer "not asked". A plan that deferred the DDL does not read sys_metadata, sys_metadata_activation or sys_migration at boot.
    • If a read cannot be avoided, its refusal is recognised with the one predicate, isMissingTableError. ⛔ No second message regex. ⛔ Don't demote every refused read.
    • Trace the two untraced readers first (sys_metadata ×4, sys_metadata_activation ×1), and name each one in the report.
    • Pins:
      • os migrate plan on an absent file prints 0 DATABASE_ERROR lines;
      • a real refusal on an existing table still warns (the control);
      • the plan's output is unchanged.
  2. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01Ujdtvqs7ree7WyQmEDwEnG
    Account: os-litant (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-20821-migrate-plan-deferred-reads
    Worktree: objectstack-issue-20821
    Domain: domain:engine
    Seat: domain:engine#2
    File surface: triage's direction 5908818119 — the dry run does not read a table whose DDL it deferred; failing that, the refusal is recognised with the one predicate isMissingTableError.

    • The boot readers that os migrate plan reaches on an absent database. Each is traced and named in the report before any edit:
      • ObjectQL.announceOpenMigrationGates in packages/objectql/src/engine.ts, its region only, reached from packages/objectql/src/plugin.ts at kernel:bootstrapped;
      • the readers of sys_metadata (×4) and sys_metadata_activation (×1), wherever they live in packages/metadata* or packages/runtime.
    • The plan command's deferral signal: packages/cli/src/commands/migrate/ is a declared cross-lane touch (domain:cli), and packages/runtime only if the signal must pass through it.
    • Pins beside the CLI's migrate dry-run suites:
      • an absent file prints 0 DATABASE_ERROR lines;
      • a real refusal on an existing table still warns (the control);
      • the plan's output is unchanged.
    • .changeset/20821-*.md.

    Stop on breach and explain in the report.

  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20821,
      "status": "needs_decision",
      "branch": "claude/issue-20821-migrate-plan-deferred-reads",
      "pr": null,
      "session": "session_01Ujdtvqs7ree7WyQmEDwEnG — mode:subagent, the PM's session id; this run is told apart by the branch",
      "premise_still_valid": true,
      "summary": "Stopped before the first edit, as the dispatch requires: the fix measures into packages/drivers/driver-sql/src/sql-driver.ts, and the 'not asked' route would need a new contract member (Clause-2 would change). The premise holds: on an absent file the plan's boot reads tables whose DDL it deferred. The 6 lines the card names come from 6 readers, all traced at b253fadfb. (1) sys_metadata: ObjectQLPlugin.restoreMetadataFromDb, then ObjectStackProtocolImplementation.loadMetaFromDb (metadata-protocol), reading state active and organization_id null, in ObjectQLPlugin.start() phase 2. (2) sys_metadata: readAuthoredTranslationLayer (core fallbacks/authored-translation-sync.ts), from wireAuthoredTranslationSync's kernel:ready hook, type translation. (3) sys_metadata: ObjectQLPlugin.readAuthoredHookRows via resyncAuthoredHooksNow at kernel:ready, type hook. (4) sys_metadata: ObjectQLPlugin.readAuthoredActionRows via resyncAuthoredActionsNow at kernel:ready, type action. (5) sys_metadata_activation: ObjectStoreActionActivationStore.probe (core utils/metadata-activation-store.ts), via ObjectQLPlugin.hydrateActionActivations at kernel:ready. (6) sys_migration: ObjectQL.announceOpenMigrationGates, then readMigrationFlagVerified, then findOne for id adr-0104-value-shapes, at kernel:bootstrapped. Each reader already handles the refusal: loadMetaFromDb uses isMissingTableError, (2) to (4) log at debug and return null, (6) answers not verified, and (5) logs its own functional warn. The DATABASE_ERROR line itself is written inside the driver. H1: at b253fadfb the count is 12, not 6. The other 6 come from app-crm's own onEnable hook on kernel:bootstrapped (registerCrmPositionBindings), which reads sys_position x3 and sys_permission_set x3, tables the plan's composition never declares. `os migrate apply --yes` on an absent file prints the same 12. A plan on the file that apply just migrated prints exactly those 6 app-hook lines. I did not bisect why the card counted 6. H2: the deferral signal is SqlDriver.setDeferredDdl(true), armed by the CLI's DeferSchemaDdlPlugin.init() (packages/cli/src/utils/schema-migrate.ts). It is held in the protected fields deferredDdl and deferredSchemaObjects. No IDataDriver member carries it, and neither does any engine or kernel key. skipSchemaSync means the opposite: DDL runs out of band and the tables are assumed present. So none of the 6 readers can see the signal when it reads. Deferred is also not absent. Under deferral, initObjects records every object before any hasTable call. A reader keyed on deferral would therefore skip existing sys_metadata rows on an existing database, and overlay objects would drop out of the plan (a code reading, not measured). The only absence fact that costs nothing is the sqlite absent-file getter sqliteOpenedEmptyInMemory, which is on SqlDriver, not on IDataDriver. Postgres and MySQL would need a catalog probe. H3 held: SqlDriver.backendStatementFault (sql-driver.ts, near line 10074) writes the warn before any reader sees the error, so a reader-side isMissingTableError cannot remove the line. H4 held: an absent ledger already answers verified false, conclusive false (the catch in readMigrationFlagVerified). The base plan prints the open value-shape gate announcement, and both a skipped read with that answer and a demoted refusal leave it unchanged. H5: I simulated the driver-side route at runtime through a scratchpad instrument, with no repo edit. All 6 of the card's lines were demoted. The 6 app-hook lines still warn. Human stdout and the --json payload are identical (see tests). The card's assignee os-litant was not touched.",
      "tests": "No code was changed, so no package suite ran. Measurements: (a) build: os-verify-lock.sh -c \"pnpm --workspace-concurrency=2 --filter '@objectstack/example-crm^...' build\", VERDICT command-exit 0, held the lock 284s. (b) H1 at b253fadfb in examples/app-crm: node ../../packages/cli/bin/run.js migrate plan --database-url file:ABSENT1.sqlite exits 0. It prints 12 DATABASE_ERROR lines on stderr: sys_metadata 4, sys_metadata_activation 1, sys_migration 1, sys_position 3, sys_permission_set 3. No file is left behind. A stack was captured for every line with a scratchpad preload that wraps console.warn. (c) os migrate apply --yes on an absent file prints the same 12. os migrate plan on that applied file prints 6, all sys_position or sys_permission_set. That is the control: the card's three tables print 0 lines once they exist. (d) Driver-side simulation (scratchpad ESM --import that wraps SqlDriver.prototype.backendStatementFault; nothing in the repo edited): DATABASE_ERROR goes from 12 to 6, and the instrument printed 'total demoted=6' (sys_metadata x4, sys_metadata_activation, sys_migration). Human stdout, normalised for timestamps, durations and file name, gives diff exit 0 against the base run. The plan-report block hashes to md5 3a74e7bb6238580ba46ae2c5e40f4133 on both runs. The --json payload without duration hashes to md5 64661190d15927a8464e0028fab5d64b on both (total 0, pending 15, managedTables 15). (e) node scripts/check-driver-conformance.mjs before: 'OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt' (exit 0). After: not applicable, because nothing was edited. (f) node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 'this branch changes nothing against origin/main (merge base b253fadfb) — nothing to derive' (exit 2), so no gate is owed and none ran. NOT MEASURED: postgres and mysql, reason: not provisioned in this container. Neither route was run against a live server dialect.",
      "mcp_calls": "0",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/20821/comments (this os-dev-report, written with scripts/pm/post-stamped.mjs through the fleet-write relay). The git push of the empty branch is not a REST write. No pr_create and no label-write ran: no PR was opened, because the run stopped before its first edit.",
      "open_questions": [
        {
          "question": "Where does the fix land? The warn is written inside SqlDriver.backendStatementFault before any reader sees the error. No contract member tells a boot reader that a table's DDL was deferred. The two honest routes are therefore a driver-side demotion in driver-sql, or a new contract fact that every boot reader asks.",
          "options": [
            "A — driver-side, keyed on the driver's own deferral. In SqlDriver.backendStatementFault, a refusal goes to debug instead of warn only when all three hold: this driver has DDL deferred, the targeted table is one whose DDL it deferred (deferredSchemaObjects), and isMissingTableError(envelope, object) is true. The throw and the envelope are unchanged. Cost: one condition beside PR #20818's scope check in sql-driver.ts, near lines 10030 to 10085. That region is disjoint from every PR #20988 hunk (lines 91, 4402 to 5051, and 15435 to 17312, read from that PR's patch). It needs a driver unit pin with three cases: deferred and missing goes to debug; a malformed read on an existing deferred table still warns; a missing table outside the deferred set still warns. It also needs a CLI pin beside the migrate dry-run suites: 0 lines on the card's three tables, with stdout and --json unchanged. A patch changeset for driver-sql, and Clause-2 stays no. Measured by simulation, it removes exactly the card's 6 lines and leaves both outputs identical. It also covers the boot of os migrate apply and every dialect, not only sqlite. Business need: real. It is the first plan or apply of every new deployment. Long-term soundness: it keys on a fact the driver recorded itself, uses the one shared predicate, adds no vocabulary, and is the same family as PR #20818. Its weakness is that it demotes a question still asked. It also keeps one stdout WARN on a dry run ('sys_metadata_activation is registered but could not be read — packaged-ACTION enable/disable is UNAVAILABLE'), which the stdout-identity pin requires anyway. AI-proofing: it adds no public or authorable surface. Startup scope: it is the smallest option, with no new gate and no contract growth.",
            "B — 'not asked'. Declare a fact the readers can ask (an optional IDataDriver member, or an engine or kernel key), and make the 6 readers consult it: the ObjectQL plugin x3, the ObjectQL engine, core's translation sync and metadata-protocol. The fact has to mean absent, not deferred (see H2). That is free only on the sqlite absent-file path; Postgres and MySQL need a catalog probe, which is the new public driver method PR #20818 declined. Cost: a contract member and an api-surface regeneration, Clause-2 becomes yes (a minor changeset), and edits to engine.ts, which takes merges daily. Six call sites change, and every future boot reader must remember to ask, an invariant that no gate holds. It would also remove the stdout activation WARN. Business need: the same, but fully honest only for sqlite. Long-term soundness: it is the cleaner end state, but it spreads a plan-mode check across every boot reader. AI-proofing: it adds an optional driver member that a driver can declare and not honour. Startup scope: it grows the contract for p3 warn noise.",
            "D — demote every missing-table refusal while DDL is deferred, with no deferred-set check. It would also hide the 6 app-hook lines. But those lines name tables that nothing in this boot declared, so they carry information. It goes against triage's 'Don't demote every refused read'."
          ],
          "recommendation": "A. It meets triage's fallback exactly: one predicate, no second regex, and only the refusals for tables whose DDL this driver itself deferred. It meets all three pins (measured by simulation) with no contract growth, which keeps Clause-2 at no. B would buy 'not asked' with a new public member, and it is fully honest only for sqlite. Routing for the PM: the edit lands in driver-sql (seat 1's lane, where PR #20988 is in flight but textually disjoint). Either re-route the card, or widen this claim's surface to SqlDriver.backendStatementFault plus one driver test. The CLI pin stays in packages/cli/src/commands/migrate/."
        },
        {
          "question": "Does the card's pin 'os migrate plan on an absent file prints 0 DATABASE_ERROR lines' cover app-crm's own hook lines? At b253fadfb, 6 of the 12 lines come from examples/app-crm's onEnable kernel:bootstrapped hook. It reads sys_position and sys_permission_set, which no route on this card can reach.",
          "options": [
            "A — scope the pin to the tables whose DDL the plan deferred: 0 lines on sys_metadata, sys_metadata_activation and sys_migration. Track the app-hook door separately (out_of_scope_findings[0]).",
            "B — widen this card to the app-hook door as well. That is a different mechanism: a declaration boot running a host hook that reads tables the plan's composition does not declare."
          ],
          "recommendation": "A, because it is a different door with a different owner. The plan on a file that apply just migrated still prints those 6 lines, so they are not a deferred-DDL artefact."
        }
      ],
      "out_of_scope_findings": [
        "class: a · reach: `os migrate plan --database-url file:X` on examples/app-crm, where X is the file `os migrate apply --yes` just migrated, prints on stderr 6 `[sql-driver] DATABASE_ERROR` lines (sys_position x3, sys_permission_set x3) and 2 'Paged read ... is NOT deterministic' warns, and on stdout 6 WARN '[crm] position binding lookup failed' lines plus 3 'skipped (row missing)' lines. Measured at b253fadfb. On an absent file the same lines come on top of the card's 6. · evidence: examples/app-crm/objectstack.config.ts onEnable, then registerCrmPositionBindings (examples/app-crm/src/security/bind-position-sets.ts:111), which hooks kernel:bootstrapped. The hook runs on the plan's declaration boot (stdout: 'Executing runtime.onEnable') and reads plugin-security tables the plan's composition does not declare ('Examined 15 managed table(s)', neither listed), so apply never creates them. · dedupe words: `migrate plan app hook kernel:bootstrapped sys_position DATABASE_ERROR` · `declaration boot onEnable hook reads undeclared table` · `position binding lookup failed migrate plan` · `migrate apply plan composition plugin-security tables`",
        "carrier: this card's routing decision (option B would remove it, option A keeps it under the stdout-identity pin) · noted, not filed — on a plan against an absent file, ObjectQLPlugin.hydrateActionActivations also prints a stdout WARN, 'sys_metadata_activation is registered but could not be read — packaged-ACTION enable/disable is UNAVAILABLE on this deployment ... Check that schema sync ran for its datasource'. It is the same deferred read, and the warning is a functional false alarm on a dry run. It is not a DATABASE_ERROR line, so it is outside the card's pin."
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat answer — #20821's two open questions (report 5924224857): Q1 A, Q2 A; in-seat, open to veto · claim surface amended

    domain:engine#2 (seat post #20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T03:42Z. Open to the maintainer's veto; a veto lands before the PR is queued.

    Governing text: triage's direction 5908818119, verbatim: "Prefer "not asked" … If a read cannot be avoided, its refusal is recognised with the one predicate, isMissingTableError. ⛔ No second message regex. ⛔ Don't demote every refused read."

    Q1 — where the fix lands: A, driver-side, keyed on the driver's own deferral

    Q2 — the pin's scope: A, the deferred tables only

    四轴(中文)

    • 实际业务需求:每个新部署第一次 plan / apply 都会看到 6 条假告警,实测可达。
    • 长远合理性:只认驱动自己记下的「这张表的 DDL 被我推迟了」这一事实,用唯一的缺表谓词;不新增契约词汇。代价是问题仍会被问,只是不再告警。这一点写明。
    • 防 AI 写错:不加任何可编写或公开的面;三个条件缺一即照常告警,真拒绝不被吞。
    • 不扩散:一个条件加 pin;B 为 p3 噪声扩契约,不取。

    Claim surface amended (claim 5924062757)

  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 20821,
      "status": "done",
      "branch": "claude/issue-20821-migrate-plan-deferred-reads",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21093",
      "session": "session_01Ujdtvqs7ree7WyQmEDwEnG — mode:subagent, the PM's session id; this run is told apart by the branch",
      "premise_still_valid": true,
      "summary": "Round 1, implementing seat answer 5924276655 (Q1 A, Q2 A). The change is in SqlDriver.backendStatementFault (packages/drivers/driver-sql/src/sql-driver.ts), in the warn decision only. A refused statement now goes to logger.debug instead of warn only when all three hold: this driver has DDL deferred, the targeted table is in its own deferredSchemaObjects, and isMissingTableError(envelope, object) is true. The throw and the envelope are unchanged. There is no second regex, and no demotion outside those three conditions. PR #21093 is a draft at ea9309b80, merged with origin/main twice (at c6aff7c0c, and at ea9309b80 just before pr_create). It is assigned to os-litant. The labeler added documentation, size/m, tests and tooling, and I set none of them. The PR body names two things as kept: the stdout WARN from hydrateActionActivations, which the stdout-identity pin requires, and the app-hook lines, which belong to #21054 and are not addressed here. The changeset is a patch for @objectstack/driver-sql only. @objectstack/cli ships dist only, and tsconfig.build.json excludes src/**/*.test.ts, so the pin is not published. The six readers are unchanged from the round-0 report: restoreMetadataFromDb/loadMetaFromDb, readAuthoredTranslationLayer, readAuthoredHookRows and readAuthoredActionRows on sys_metadata; ObjectStoreActionActivationStore.probe on sys_metadata_activation; announceOpenMigrationGates/readMigrationFlagVerified on sys_migration. One deviation in the CLI pin: src/ is cli's tsconfig rootDir, so the pin cannot import test/helpers/serve-process.ts. It names bin/run-dev.js itself and strips the same two env families that childEnv() strips (fd9f151a4). CI was still running when this report was written: 11 check runs completed with no failure, 20 in_progress.",
      "tests": "examples/app-crm, node ../../packages/cli/bin/run.js migrate plan [--json] --database-url file:ABSENT, base 576afc17b against head ea9309b80 (rebuilt). DATABASE_ERROR lines go from 12 to 6. Lines naming sys_metadata, sys_metadata_activation or sys_migration go from 6 to 0. Stderr differs by exactly those 6 lines removed and 0 added. Human stdout normalised for timestamps and Nms gives diff exit 0. The plan block hashes to md5 e102064c7b13bce96303c1e9b698693d on both. --json stdout without duration is byte-identical (cmp exit 0). No file is left behind. A gate run had written the gitignored examples/app-crm/dist/objectstack.json; the after-run was taken with it removed (see the PR's Acceptance notes). New driver pin, sql-driver-20821-deferred-ddl-missing-table.test.ts: (1) deferred and missing goes to debug; after the flush the read answers [] with nothing logged. (2) A malformed read (40,000 variables) on an existing deferred table still warns. (3) A missing table outside the deferred set still warns. Each case asserts code DATABASE_ERROR and status 500. 3 of 3 pass. New CLI pin, src/commands/migrate/plan.deferred-reads.integration.test.ts (integration tier, spawns bin/run-dev.js on a host-config fixture that reproduced 4/1/1 on the base). Human mode and --json each pass with 0 lines on the three tables, the [value-shape] announcement and the activation WARN present, the three tables still pending create_table, and no file written. Ablation 1, through scripts/ablation-replace.mjs at baf0d12ff: removing the deferred-set condition (anchor 1 to 0, blob c626b59d to fecb8704) turned the driver pin red on (3) only ('expected [] to have a length of 1 but got +0'). The restore put the blob back equal to HEAD with git diff HEAD empty. Ablation 2, at fd9f151a4, on the committed pin: removing the whole debug branch (blob to e7acc7e7), rebuilding driver-sql, and running ablation-dist-preflight --absent showed the marker absent from all 6 built files. The CLI pin was red on both cases ('expected [ …(6) ] to deeply equal []'). The restore put the blob back equal to HEAD; after a rebuild the preflight found the marker in 2 built files and the tree clean, and both pins were green. Suites at ea9309b80: driver-sql vitest run, 204 files passed and 11 skipped, 3285 tests passed and 188 skipped (the live PG and MySQL cells). cli --project unit: 240 files and 3419 tests passed. cli --project integration on this PR's file plus schema-migrate.readonly-probe and schema-migrate.deferred-ddl: 3 files and 9 tests passed. NOT MEASURED: the full cli integration tier, reason: the 10-minute foreground cap ended it (exit 124 at 595s); declared to CI. Typecheck is green for driver-sql and cli, including check:test-typecheck with debt unchanged. Earlier run at baf0d12ff: pnpm --filter @objectstack/driver-sql test -- --maxWorkers=2 passed the whole package (203/11 files); the flag after the bare -- was dropped, and that run is superseded by the run above. Gates: dispatch-gates --commands at ea9309b80 derived 66 families. All 66 ran, with exit codes captured before any pipe, and all exited 0. --ran reports '66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN'. On the earlier head, check:dual-build-cjs-loads and check:i18n-coverage first returned PREREQUISITE NOT MET (exit 3) because nine packages had no dist. I built them and both gates re-ran green. The derivation flagged lint.yml and engine-double-contract.pinned.json as stale inputs. I read both diffs: timeout keys, comments, and pins for other files, so no family changes. Driver conformance before and after: 'OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt.' Lint narrowing at ea9309b80: eslint --no-inline-config --format json over the 3 touched TypeScript files reports files 3, errors 0, warnings 0. eslint --print-config resolves all 3, with no parserOptions.project. eslint.config.mjs enables no type-aware linting, so untouched files' verdicts cannot move. The control-byte scan is clean (grep exit 1). spec check:generated after the first merge: all 15 artifacts up to date.",
      "mcp_calls": "0",
      "api_writes": "This run: 4 writes, all through the fleet-write relay as repository_dispatch, then executed by .github/workflows/fleet-write.yml. Round 0: POST /repos/objectstack-ai/objectstack/issues/20821/comments (report 5924224857). Round 1: POST /repos/objectstack-ai/objectstack/pulls (pr_create, draft, #21093, body read back identical at 9967 bytes); POST /repos/objectstack-ai/objectstack/issues/21093/assignees (scripts/pm/label-write.mjs --assign os-litant, read back MATCHES); POST /repos/objectstack-ai/objectstack/issues/20821/comments (this report, through scripts/pm/post-stamped.mjs). The git pushes are not REST writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: #21054 (filed bare by the seat) · noted, not filed. The app-crm onEnable kernel:bootstrapped hook still prints 6 DATABASE_ERROR lines on sys_position and sys_permission_set on a plan against an absent file, and this PR's pin does not count them. Measured: once a compiled artifact (examples/app-crm/dist/objectstack.json) is present, the plan takes the artifact boot path, onEnable does not run, and those 6 lines disappear too, so the door depends on whether that file exists.",
        "carrier: PR #21093 body (Kept on purpose) · noted, not filed. The stdout WARN from ObjectQLPlugin.hydrateActionActivations ('sys_metadata_activation is registered but could not be read …') is still printed on a dry run against an absent file. It is a functional false alarm, kept because the stdout-identity pin requires it."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21093 @ ea9309b8 (os migrate plan on a database whose DDL it deferred no longer warns DATABASE_ERROR for the tables it deferred)

    domain:engine#2 (seat post #20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T06:22Z. Judged against GitHub, not the reports (5924224857, round-1 report).

    • Form: draft PR on main. The body opens Fixes #20821 / Clause-②: no, its only closing keyword.

    • Scope: 4 files, inside claim 5924062757 as amended by the seat answer 5924276655:

      • driver-sql's SqlDriver.backendStatementFault: a 29-line early return in the warn decision only;
      • one driver unit test;
      • one CLI integration pin under packages/cli/src/commands/migrate/;
      • .changeset/20821-plan-deferred-ddl-reads.md: @objectstack/driver-sql patch (cli ships dist only).

      Not governed. +418 / −0.

    • Contract review: at tier, PASS on this head (5925795690). The demotion keys on exactly the three conditions of the seat answer:

      1. the driver's own deferredDdl;
      2. the table is in its own deferredSchemaObjects;
      3. isMissingTableError.

      A miss falls through to the unchanged warn, so no condition demotes on its own. The envelope (DATABASE_ERROR / 500) and the throw are unchanged. There is no regex, and triage's two ⛔ hold. The hunk is disjoint from the JSON-column gate, from the JSON-membership helpers and from merged PR fix(plugin-security,driver-sql,driver-turso): lower type-blind at the RLS seam without a guard, then delete the F1/F2 whole-day and NOT-rewrite copies (#5930 step 4, group 2) #20988. No public surface moves.

    • CI on ea9309b8: 34 check-runs. 31 success and 3 skipped, all skips on the roster (check-expected-skips --pr 21093: OK, exit 0). Merges cleanly onto main (git merge-tree).

    • Tests (dev's evidence):

      • On examples/app-crm against an absent file, DATABASE_ERROR drops from 12 to 6. The 6 lines naming sys_metadata, sys_metadata_activation and sys_migration drop to 0.
      • Human stdout (normalised) and --json output are byte-identical to the base.
      • Driver pin: 3 of 3 cases. CLI pin: human and --json, both 0 lines on the three tables.
      • Ablations: removing the deferred-set condition turned case 3 red; removing the debug branch turned the CLI pin red. Both were restored and the restores proven.
      • driver-sql: 3285 passed. cli unit: 3419 passed.
      • Gates: 66 of 66, exit 0.
      • Driver conformance: 50 / 0 / 0.
    • Findings:

    • Landing: this seat readies and arms the PR through the queue.

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed — PR #21093 as cf0346ec7 (the card closes)

    domain:engine#2 (seat post #20966) · session_01Ujdtvqs7ree7WyQmEDwEnG · 2026-10-01T06:48Z.

    • Merged through the merge queue at 2026-10-01T06:47Z as squash cf0346ec7 on main. Two readings agree: the PR object and origin/main's log. The squash carries the PR's 4 files (418 insertions, 0 deletions).

    • Records it landed on:

      • seat answer 5924276655 (Q1 A, Q2 A);
      • contract review PASS 5925795690 @ ea9309b8;
      • the ACCEPT on this card.
    • What changed for operators: os migrate plan, and apply's boot, against a database whose DDL the driver deferred no longer warns DATABASE_ERROR for sys_metadata, sys_metadata_activation or sys_migration. Those refusals log at debug. Three conditions must all hold:

      • the driver's own deferral;
      • its own deferred-table set;
      • isMissingTableError.

      Every other refused read still warns. The plan's output is unchanged.

    • The card closes completed by the PR's Fixes #20821. pm:dispatched is removed in the same act as this record.

    • Still open, elsewhere: the app-crm onEnable hook's 6 lines are [finding] os migrate plan on examples/app-crm runs the app's onEnable hook, which reads sys_position / sys_permission_set the plan never declares: 6 DATABASE_ERROR + 6 WARN lines on every plan #21054 (domain:cli).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:enginepriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions