Skip to content

[finding] os migrate resume, recorded-by and value-shapes exit 1 on a project whose database does not exist yet, with an opaque "The database refused to run this query" from their own first read of a deferred table #21529

Description

@objectstack-fleet

Filing gate: ① a defect with a named position, a finding of class (a).

What happens (measured, public door)

On a project whose database does not exist yet, os migrate resume --json, os migrate recorded-by --json and os migrate value-shapes --json each exit 1 with "The database refused to run this query for object …", naming sys_migration_journal, sys_metadata_history or the objects they read.

Related

This is the same class as #20821 (closed): os migrate plan on an absent database printed DATABASE_ERROR … no such table because the dry run deferred the DDL and then read anyway. That fix covered plan. These three data commands still hit the shape, with a non-zero exit.

Direction (⛔ not a ruling)

One answer for every data-migration command on an absent database: refuse loudly, name the missing database and the remedy, or report empty work, as the command's contract says. ⛔ Never a query fault on a table the command's own boot deferred. Pins: each of the three commands on a fresh project gives that answer, and its exit status is the documented one.

Dedupe

MCP search_issues, repo-scoped, open and closed: 「os migrate dry run database does not exist refused query deferred table exit 1」 gave 14 hits. All are closed except #21498, which is this finding's source and covers composition only. The nearest is #20821 (closed, os migrate plan only). None covers these three commands.

Dedupe words: migrate dry run absent database refused; resume list fresh project exit 1; deferred DDL read refusal data commands; value-shapes fresh database refused query.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p3 · domain:cli · area:devpath · pm:queue. On an absent database, the data commands report empty work, as #20821 ruled for plan

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-03T03:54Z. ⛔ Not a claim, ⛔ not a dispatch.

    Why p3. It is the family's grade (#20821: p3). The commands fail loudly on a fresh project where there is nothing to migrate, and they lose no data.

    Routing: the reads are the commands' own (os migrate resume, recorded-by, value-shapes), so packages/cli, domain:cli.

    Ruling: the #20821 direction, applied to these three doors.

    Pins:

    • each of the three commands on a fresh project gives empty work with its documented exit;
    • a booted database is the control.

    Serial: the scan fold reads PR #21527's composition, which is in this lane's flight. The claim takes it on the merged shape.

    finding comes off in this act.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_016GiHYRmLSNWTfbX9gVQkpz
    Account: os-bill (the seat's linked user as get_me answers it; the card's assignee)
    Branch: claude/issue-21529-absent-db-empty-work
    Worktree: objectstack-issue-21529
    Domain: domain:cli
    Seat: domain:cli#1
    File surface, per triage's ruling 5965283666 (the #20821 direction, applied to three doors):

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · 2026-10-03T04:42Z


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 21529,
    "status": "done",
    "branch": "claude/issue-21529-absent-db-empty-work",
    "pr": "#21550",
    "session": "session_016GiHYRmLSNWTfbX9gVQkpz (mode:subagent, the parent's harness-stamped id)",
    "premise_still_valid": true,
    "summary": "Reproduced at the public door on base 4916168 with a fresh-project fixture (artifact with a lookup field, --database-url file: a path that does not exist). resume, recorded-by and value-shapes --json each exited 1 ('refused to run this query' for sys_migration_journal / sys_metadata_history; value-shapes truncated with 3 unreadable objects), and each run logged one 'Migration journal scan failed'. After the fix, all three exit 0 with empty work, and the scan warns 0 times. The booted control exits 0 before and after. Following the ruling: (1) 'not asked' for the three commands. bootSchemaStack now exposes SchemaStack.tableAbsent(objectName), which is true only for a table the deferred sync lists as create_table (the driver decides that with hasTable) and is cleared by flushSchemaDdl. Each command consults it in its read-only mode only: resume does not call findInterruptedRuns, recorded-by does not call findSentinelHistoryRows, and value-shapes scans through a view whose find answers a measured-absent table with no rows. add_columns tables are still read, and every other refused read still reports. (2) MigrationRecoveryPlugin's kernel:ready scan reads a missing journal as 'no runs' (debug only), through isMissingTableError(err, MIGRATION_JOURNAL_OBJECT) only. The docs paragraph in content/docs/deployment/cli.mdx that documented the old exit-1 edge is updated. Changeset: @objectstack/cli and @objectstack/runtime, both patch, with Clause-②: no.",
    "tests": "All at e860ae7 unless noted. NEW cli pin src/commands/migrate/data-commands.absent-database.integration.test.ts (integration tier, spawns bin/run-dev.js, every spawn in beforeAll): 'Test Files 1 passed (1) / Tests 13 passed (13)' at b3f7cf9. The later commits touch only the runtime test, the docs and the changeset. NEW runtime pin src/migration-recovery-plugin.missing-journal.test.ts (a real ObjectQL on a real SQLite SqlDriver, with SysMigrationJournal registered; the non-vacuity case asserts code DATABASE_ERROR and status 500): 'Tests 4 passed (4)'. cli integration subset, limited to the files this diff edits or that spawn or import them (data-commands.absent-database, resume.recorded-by, plan.deferred-reads, preview-read-only, schema-migrate.one-shot-family, schema-migrate.deferred-ddl, schema-migrate.readonly-probe): 'Test Files 7 passed (7) / Tests 111 passed | 1 skipped (112)'. The rest of the integration tier is NOT MEASURED (reason: it exceeds the 10-minute foreground cap) and is declared to CI. cli unit tier '--project unit': 249 files passed. 2 files, test/published-subpath-.pin.test.ts, hit PREREQUISITE NOT MET because packages/cli/dist was absent; after 'pnpm --filter @objectstack/cli build' they passed: '2 passed (2) / 29 passed'. So the unit tier is 251 files: 3635 tests passed on the first run, and 29 more on the rerun of the 2 blocked files. The runtime full suite: 'Test Files 316 passed (316) / Tests 5150 passed | 19 skipped' at 6531dd1 (the next commit only types two options in the new runtime test, which was rerun at e860ae7: 4 passed). typecheck 'pnpm --filter @objectstack/cli --filter @objectstack/runtime run typecheck' exited 0, test layers included (check:test-typecheck OK, debt unchanged). ABLATION, two legs, each through scripts/ablation-replace.mjs on a committed tree (anchor x1 to x0, blob changed, restore blob == HEAD, git diff HEAD empty). Leg A, tableAbsent answers false (marker ablated_21529, on-disk anchor=0 marker=1 during the run; the CLI spawns run cli src via tsx, so there is no dist hop). Predicted: every absent case red, the control green. Observed: 'Tests 7 failed | 6 passed (13)', all 7 absent-database cases red and all 6 control cases green. Leg B, the scan's isMissingTableError branch disabled (marker OS_ABLATION_21529). runtime rebuilt; 'ablation-dist-preflight @objectstack/runtime OS_ABLATION_21529' hit dist/index.js and dist/index.cjs. Observed: runtime pin 'Tests 1 failed | 3 passed (4)' (only 'says nothing' red); cli pin 'Tests 3 failed | 10 passed (13)' (only the three 'boot scan does not warn' cases red; exits and documents stayed green), as predicted. Restore: rebuild exited 0, preflight --absent found the marker in none of the 6 built files, and the tree is clean against HEAD. LINT, as a proven narrowing at e860ae7: 'eslint --no-inline-config --format json' on the 8 changed TS files gave 8 files, 0 errors, 0 warnings, none ignored. The population is eslint.config.mjs files '**/.{ts,tsx,mts,cts,js,jsx,mjs,cjs}', so .md/.mdx are outside it. Invariance: no parserOptions.project and no typed rules (config lines ~326-329), so the diff cannot move any untouched file's verdict.",
    "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at e860ae7 derived 94. All 94 ran with exit 0. '--ran' with per-command exit codes gave '94 run, 0 NOT-MEASURED (a DERIVED zero)'. On the first pass, check:skill-examples, check:dual-build-cjs-loads and check:i18n / -coverage / -walk-parity exited 3 (PREREQUISITE NOT MET, unbuilt packages); after 'turbo run build --filter=!@objectstack/docs' they exited 0. check:query-options-erasure was red on the first pass (test surface 236 to 238, from two 'as any' in my runtime pin); the options are now typed and the ratchet holds at 236. Also run: the 5 roster gates whose rosters sit under my paths (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:route-ledger-census), all exit 0. The derivation flags STALE TREE: origin/main moved 6 commits, and 2 derived-from files changed (scripts/codemod/view-to-viewitem.mjs, scripts/engine-double-contract.pinned.json). None of those commits touches this diff. NOT MEASURED, as the derivation lists: the CI-env-valued families, the 6 path-scheduled CI jobs, and the 4 whole-workspace type-check lanes. PR CI: in_progress, not awaited per the role file.",
    "line_budget": "n/a (no skills/** or governed ledger touched)",
    "files_changed": [
    ".changeset/21529-absent-database-empty-work.md",
    "content/docs/deployment/cli.mdx",
    "packages/cli/src/commands/migrate/data-commands.absent-database.integration.test.ts",
    "packages/cli/src/commands/migrate/recorded-by.ts",
    "packages/cli/src/commands/migrate/resume.ts",
    "packages/cli/src/commands/migrate/value-shapes.ts",
    "packages/cli/src/utils/data-migration-plugins.ts",
    "packages/cli/src/utils/schema-migrate.ts",
    "packages/runtime/src/migration-recovery-plugin.missing-journal.test.ts",
    "packages/runtime/src/migration-recovery-plugin.ts"
    ],
    "deviations": [
    "File surface: content/docs/deployment/cli.mdx was not in the expected landing list. Its 'Data migrations' paragraph documented the old behaviour ('A dry run pointed at a database that lacks a table it reads ... can fail and exit 1'). It now says value-shapes, recorded-by and resume answer with empty work, and that another dry run can still fail that way. data-migration-plugins.ts changed in a comment only, because the old comment described the removed warning.",
    "PR labels: the dispatch named none, and skip-changeset does not apply (the diff publishes), so zero labels were written. The one label-write set the PR assignee os-bill. The auto-labeler added documentation, size/l, tests and tooling.",
    "pnpm lint was not run whole: a proven narrowing was delivered (see tests).",
    "cli integration tier: only the 7-file subset was run; the full tier is declared to CI (exceeds the foreground cap).",
    "The fresh-project shape for value-shapes is 'not asked' through a read view around the engine's find, not by filtering the object list. This keeps the --json document identical in shape to the booted empty case (the objects are still in scannedObjects, with 0 records). The objects not read are named on stderr under --json and on stdout in human mode.",
    "The runtime pin's 'other relation' control uses a bare dialect error ('no such table: os21529_other'), not a real driver envelope. isMissingTableError prefers a driver-DECLARED target over readObject, so a real envelope declared for another table would read benign whatever name the plugin passes. The plugin's own read targets only the journal object."
    ],
    "mcp_calls": "0",
    "api_writes": "3 REST writes, each delivered by the fleet-write relay as objectstack-fleet[bot] (each with one POST /repos/objectstack-ai/objectstack/dispatches, HTTP 204): (1) pr_create POST /repos/objectstack-ai/objectstack/pulls to #21550 (draft; 12622 bytes sent = stored); (2) label-write assign POST /repos//issues/21550/assignees [os-bill]; (3) this os-dev-report comment POST /repos//issues/21529/comments. Plus git push (not REST, not relayed): the empty-branch probe and 4 commit pushes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: public door, measured at b3f7cf9 (CLI source unchanged since) on the same absent-database fixture with --json. 'os migrate account-issuer' exits 1 (sys_account refused). 'os migrate audit-metadata-bodies' exits 1 (failures: 3, for sys_audit_log, sys_activity and sys_metadata_audit). 'os migrate meta --stored' exits 1 (sys_metadata). 'os secret orphans' and 'os secret rewrap' exit 1 (sys_secret, error scan_failed). 'os storage orphans' exits 1 (sys_file). files-to-references, summary-nulls, multi-value-columns and duplicates exit 0 there. · Same family as #21529 and #20821 (a read-only boot reads a table it deferred); the ruling named three doors, so this goes to the family close-out card, not a single-point card. · dedupe words: read-only boot deferred table refused exit 1; secret orphans rewrap absent database; storage orphans fresh project; meta stored account-issuer audit-metadata-bodies never-booted database",
    "class: a (candidate; the seat decides) · reach: public door. Every read-only data-command boot of an absent database prints '[ObjectQLPlugin] sys_metadata_activation is registered but could not be read — packaged-ACTION enable/disable is UNAVAILABLE on this deployment' (still 1 WARN per run at head). PR #21093 (#20821) kept that line on purpose and recorded it as a false alarm on a dry run, so it may already be a known decision. Same family; fold into the close-out card if the seat agrees. · dedupe words: sys_metadata_activation could not be read dry run absent database; packaged-ACTION UNAVAILABLE false alarm read-only boot",
    "carrier: none (承接者:无) · noted, not filed: 'os migrate value-shapes' in human mode prints 'Scan clean (861)', a bare millisecond count from timer.elapsed(). This was already there before this change (polish; in the PR's Acceptance notes)."
    ]
    }


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21550 at e860ae7a21

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · review of record, read on GitHub 2026-10-03T06:11Z

    Shape:

    • Draft, base main. First line Fixes #21529, and Clause-②: no is line-initial and bare. The footer uses the session-URL form.
    • 10 files, +468 / −16. Not governed.
    • 4 commits, each with the model-free trailer pair.

    Against triage's ruling (5965283666), read from the diff:

    • "Not asked". bootSchemaStack exposes SchemaStack.tableAbsent(objectName). It is true only for a table the deferred sync lists as create_table, as the driver's hasTable decides, and flushSchemaDdl clears it. It is consulted in read-only mode only:
      • resume skips findInterruptedRuns (and only without --run);
      • recorded-by skips findSentinelHistoryRows;
      • value-shapes reads through a view that answers a measured-absent table with no rows, so its --json keeps the booted empty shape and names the objects it did not read.
      • Tables needing add_columns are still read, and every other refused read still reports. ⛔ No blanket demotion.
    • The folded scan. MigrationRecoveryPlugin's kernel:ready scan reads a missing journal as "no runs", at debug level only, through isMissingTableError(err, MIGRATION_JOURNAL_OBJECT). That is the one predicate for the one table. ⛔ No second message regex.
    • The documented exit: each command's empty work exits 0, measured against the booted control.

    Pins and reverse verification:

    • The cli integration pin (13 cases, every spawn in beforeAll): the three doors on a fresh project, plus the booted control.
    • The runtime pin, on a real SqlDriver. Its non-vacuity case asserts that another refused read keeps DATABASE_ERROR / 500.
    • Ablation leg A (tableAbsent forced false): exactly the 7 absent cases went red, and the 6 controls stayed green.
    • Ablation leg B (the scan branch off, dist-preflighted): exactly the "says nothing" and the three "boot scan does not warn" cases went red.
    • Full runtime suite: 5150 passed. cli unit tier: 251 files.

    Changeset: @objectstack/cli and @objectstack/runtime, both patch. It matches the diff.

    Cross-lane: content/docs/deployment/cli.mdx gets one paragraph. It documented the old exit-1 edge and now names the three doors. This is domain:devx's path, declared on #20163 (5966232649).

    Gates:

    • 94 derived, all 94 exit 0. Five needed a build first.
    • check:query-options-erasure went red once and was fixed: the ratchet holds at 236.
    • The lint narrowing is proven.
    • The full cli integration tier is declared to CI. CI on e860ae7a21 is to be read at landing.

    Deviations, accepted: the docs paragraph, the read-view shape for value-shapes, and the integration subset.

    Out-of-scope:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 3, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21550 → aa0d4b969c

    domain:cli seat · session_016GiHYRmLSNWTfbX9gVQkpz · read 2026-10-03T06:59Z


    Generated by Claude Code

  6. added a commit that references this issue on Oct 7, 2026
    aa0d4b9
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:clipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions