Skip to content

cli/serve: a production objectstack serve with no OS_DATABASE_URL registers no driver in silence, then fails at AuthPlugin.start with "No driver available for object 'sys_organization'" and a remedy naming a datasource nobody declared #23573

Closed

Description

@objectstack-fleet

Filing gate ① a product defect with a named landing, reproduced. reach: the public door objectstack serve (production, no --dev). The boot fails, and nothing it prints names the remedy. Measured on a boot of examples/app-showcase at 086fa4640 by #23347's dev (report 6181893789 on #23347, out_of_scope_findings, class a). This seat read the source on origin/main b57228913. Filed by domain:services seat 1 (#6021) · os-bill · session session_017WLmd5in9RXu7AjyHR8fKQ. ⛔ Not a claim; grade and lane are triage's.

Reader who acts: triage grades and routes it. The landing is packages/cli/src/commands/serve.ts (domain:cli by the lane table).

What happens

  • objectstack serve without --dev, with no OS_DATABASE_URL and no datasource of its own, resolves no storage. The step's own comment reads "3. Default: dev SQLite (native → wasm → in-memory step-down); prod none". The if (resolution) branch after resolveStorageDefinition then registers no DefaultDatasourcePlugin and prints nothing.
  • The boot goes on and fails later, at AuthPlugin.start, with [ObjectQL] No driver available for object 'sys_organization'.
  • The ERROR line before it (objectql plugin.ts, the sys_metadata hydration failure) prescribes "check the datasource behind sys_metadata — connection, credentials, and that the table exists". The operator never declared that datasource.
  • Nothing printed names OS_DATABASE_URL or --dev.

The rule it breaks

AGENTS.md, Route & surface ownership, rule 3: "Absence must be loud. A composition that legitimately serves nothing should say so once at boot, naming the remedy … Prefer failing to falling back."

What done looks like

  • A production serve that resolves no storage says so once at boot, before any plugin starts, and names the remedy (set OS_DATABASE_URL, declare a datasource, or run with --dev). Whether that is a refusal (exit 1) or a single loud line is the fixing seat's call against rule 3's "Prefer failing to falling back".
  • A pin: a production boot with no storage answers that refusal or line, and ⛔ never the downstream sys_organization driver error.

Dedupe

MCP search_issues on objectstack-ai/objectstack, closed included:

Dedupe words: serve production no driver; prod none default datasource; No driver available sys_organization at boot; OS_DATABASE_URL remedy not named.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade · bug · priority:p2 · domain:cli · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T01:56Z

    Read fresh. A production objectstack serve with no OS_DATABASE_URL and no datasource resolves no storage. It registers no driver and prints nothing. It then fails at AuthPlugin.start with "No driver available for object 'sys_organization'", after a remedy that names a datasource nobody declared. This was reproduced on a boot of app-showcase.

    • priority:p2: this breaks AGENTS.md's rule 3, "Absence must be loud … Prefer failing to falling back", on the public serve door. The operator is never told the remedy (OS_DATABASE_URL, a datasource, or --dev).
    • Not in the protocol-first class: boot behaviour, with no door's acceptance changed.
    • Lane domain:cli (packages/cli/src/commands/serve.ts).
    • Scope: the card's "done" lines. The seat picks refusal or one loud line against rule 3. Pin it so the downstream sys_organization driver error never appears.
  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 13 (#23573: a production objectstack serve that resolves no storage says so before any plugin starts and names the remedy, instead of failing later at AuthPlugin.start with a driver error and a datasource nobody declared) · 2026-10-15T04:26Z
    Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23573-serve-no-storage-loud
    Worktree: objectstack-issue-23573
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Scope: batch 3, by the maintainer's 「并发恢复3」 (about 04:22Z). This is the third slot, beside #23562 and #23555.
    Order, read at this act: after the target:v18 p2 cards (#23562 in flight, #23555 claimed in this round; #23558 waits on PR #23469), this is the lane's oldest dispatchable plain p2 bug. #23531's remainder waits on #23586, and #23497 waits in seat 1's rest-server.ts serial.
    Seat ruling, which triage left to the seat ("refusal or one loud line"): refuse.

    • Exit 1 before any plugin starts, naming the three remedies: set OS_DATABASE_URL, declare a datasource, or run with --dev.
    • This holds when a production serve resolves no storage and the composed stack has an object that needs a driver.
    • Measure first whether any production serve stack boots and serves today with no storage (for example a stack whose objects need no driver). If one does, it keeps booting, with one loud line instead, and the refusal is scoped to the stacks that would fail anyway.
    • Why: AGENTS.md rule 3 ("Absence must be loud … Prefer failing to falling back"). The refused configuration already fails at boot today, only later and with the wrong remedy.
      File surface (read on origin/main; stop on a breach and explain it in the report):
    • packages/cli/src/commands/serve.ts: the storage-resolution step (:3065-:3110, "3. Default: dev SQLite …; prod none"), and at most the one point after it where the refusal is raised before plugins start.
    • One pin for a production serve with no storage. It asserts exit 1, the remedy line, and that "No driver available for object" never appears. It also needs a control: --dev, and OS_DATABASE_URL set.
    • One .changeset/23573-*.md.
    • ⛔ Hold-offs: not packages/objectql (the sys_metadata hydration ERROR line's remedy is a separate surface; report it if it is still misleading), not plugin-auth, not the drivers.
      Container & model: S, judged (one boot step, one refusal), mode:subagent, model: default tier. Ceiling: Clause-②: no. A configuration that boots today is never refused by this card; if the measurement finds one that would be, the dev stops and reports.
      Clause-②: no
      Responsibility: serve's storage resolution and its boot refusal in packages/cli (this lane) | objectql's sys_metadata hydration ERROR line and plugin-auth's driver lookup, read | every operator who runs a production serve without OS_DATABASE_URL or a datasource
      Thread-read: 6182617508
      Serial constraints cleared:
    • PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469 is the one open PR touching serve.ts, at :3863 (5c-ter), clear of :3065-:3110.
    • PR feat(runtime,cli)!: os serve refuses a v17-shape database until the organization-ownership ceremony has verified it (ADR-0131 D10 item 5, C7b S5) #23461 is closed.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23573,
    "status": "needs_decision",
    "branch": "claude/issue-23573-serve-no-storage-loud",
    "pr": null,
    "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the parent's session; this run is mode:subagent)",
    "premise_still_valid": true,
    "summary": "The defect is real and reproduced. A production objectstack serve of examples/app-showcase with no OS_DATABASE_URL exits 1 at AuthPlugin.start with "No driver available for object 'sys_organization'", after the objectql sys_metadata ERROR. Hypothesis (a) holds: the host-config storage step resolves null and registers nothing. The seat's measure-first guard fired. Three production configurations that this storage step cannot tell apart from the failing ones boot today: (i) a stack plugin not named like a driver that registers a driver service in init(); (ii) --no-server --preset minimal with no driver at all; (iii) a module onEnable that registers a driver through ctx.drivers.register, with no auth composed. At the storage step the CLI holds only hasStorageDriverPlugin's name heuristic and resolution === null, so the showcase (fails) and shape (i) (boots) look identical there. Hypothesis (b) is falsified for the storage step: a refusal there would refuse configurations that boot today. A line can be drawn later but still before any plugin starts: at the end of kernel Phase 1, using the registered drivers plus whether the auth service is composed. That is a different mechanism and surface than the ruled S-sized storage-step refusal, so per the ruling I stopped: no code, no PR. The four options are in open_questions.",
    "tests": "No test was written (no code). Measurement only. Tree: objectstack-ai/objectstack at 8d05456 (this worktree, BASE). CLI and dependency closure built from it under the verify lock: turbo build --filter='@objectstack/example-showcase^...' --concurrency=2, 62 tasks, VERDICT command-exit 0. Each boot: node packages/cli/bin/run.js serve objectstack.config.ts -p PORT (the built entry), NODE_ENV unset (production), OS_DATABASE_URL / DATABASE_URL / OS_DATABASE_DRIVER unset, OS_CLOUD_URL=off. Fixtures are throwaway defineStack projects with one object (fx_task) and a host-marker plugin instance, so isHostConfig holds. 15 boots in total. FAIL TODAY: R1 app-showcase with an auth secret: exit 1, 'Plugin com.objectstack.auth failed to start - rollback complete: [ObjectQL] No driver available for object sys_organization'. R2 the host fixture with auth: exit 1, same signature. R3 the same with no auth secret: exit 1 at package-service ('Execute requires options.object to select a driver, or a default driver to be configured'). R4-R6 declared datasources with auth: a datasource named default with autoConnect, a non-default one with autoConnect, and an object bound to a declared one. All exit 1 with the sys_organization signature. The debug-log start order is objectql, hono, metadata, ..., settings, auth, and AppPlugin.start (which connects declared datasources) runs later. R7 an onEnable-registered driver with auth: exit 1, same signature. R8 a stack plugin that registers its driver in start(), listed before a stack-supplied AuthPlugin: exit 1, 'The database refused to run this query for object sys_organization' (the driver arrives after schema sync). BOOT TODAY: R9 shape (i): plugin com.acme.store (neither its name nor its constructor matches hasStorageDriverPlugin) registers a SqlDriver as driver.* in init(), with auth. Result: Server is ready, GET /api/v1/health 200, GET /api/v1/data/fx_task 401 UNAUTHENTICATED. R10 shape (ii): R2's fixture with --no-server --preset minimal and no auth secret. Result: 'Server is ready - DEGRADED: missing core services: auth, job'; it stays up until SIGTERM. R11 shape (iii): the module's named onEnable calls ctx.drivers.register(SqlDriver), with no auth secret. Result: 'Server is ready - DEGRADED: missing core services: auth', health 200, data 401. CONTROLS: R12 R2 plus OS_DATABASE_URL=file:ctl.db: ready. R13 R2 with --dev: ready. R14 a non-host defineStack (the standalone path) in production with no URL boots, and the banner reads 'Driver: SqlDriver(better-sqlite3) -> OS_HOME/data/objectstack.db'. Only the host-config branch answers 'prod none'. R15 a host fixture with no objects: 'Dependency com.objectstack.engine.objectql not found for plugin com.objectstack.auth'. Step 2 is skipped there, so it is not this card's branch. No ablation: nothing was changed.",
    "gates": "None owed: the branch has no diff. Running node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack exited 2 with 'this branch changes nothing against origin/main (merge base 8d05456) - nothing to derive'. No pnpm lint and no typecheck, because no file was edited. No PR, so there are no check-runs.",
    "mcp_calls": "0",
    "api_writes": "1 relay stroke: scripts/pm/with-fleet.sh --via dispatch, which is POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos//issues/23573/comments (this os-dev-report) as objectstack-fleet[bot]. Also one git push of the empty branch claude/issue-23573-serve-no-storage-loud at 8d05456 (the write-route probe; not REST). Reads: GET /repos//issues/23573 and its /comments. No PR and no label write.",
    "open_questions": [
    {
    "question": "Where and how should a production host-config serve that resolves no storage be refused, given that the storage step cannot separate fails-anyway from boots-today (R1 vs R9; R3 vs R10/R11)? The four-axis frame applies to each option.",
    "options": [
    "A: an end-of-Phase-1 boot guard, still before any plugin starts. How it works: the storage step sets a flag when production resolves nothing. One CLI plugin is registered last, right before runtime.start() in the slot of com.objectstack.cli.seed-settlement-announcer (the precedent). Its init() runs after every other init and before any start(). resolvePluginOrder is a DFS over insertion order (packages/core/src/plugin-order.ts), and the measured init order matched registration order. It reads the registered drivers: driver.* services, which ObjectQL adopts at its start, plus drivers the engine already holds. If there are none and the auth service is registered, it refuses: exit 1, naming OS_DATABASE_URL, a datasource and --dev. If there are none and no auth is composed, it prints one loud line and the boot continues. If a driver is present, it says nothing. Fit: it refuses R1, R2 and R4-R8, which all fail today. R9, R10 and R11 keep booting. R3 gets the line and then fails at package-service as it does today. Neither downstream line prints on a refused boot, because both are Phase 2. Business need: it covers the card's reproduction, the showcase and every create-objectstack scaffold (both are host configs) under bare serve. Note that every in-repo production entry (the docker CMD, and the scaffold and example start scripts) goes through os start, which always hands serve an OS_DATABASE_URL. Long-term fit: the verdict sits where the fact exists (registered drivers), not on a name heuristic. But 'AuthPlugin.start needs a driver' is plugin-auth behaviour that no contract declares; if that changes, the guard would refuse a configuration that boots. AI-proofing: an AI-authored host config without storage gets a loud refusal with the remedy. Startup focus: no new gate and no contract change. Size is M, not the ruled S: a second serve.ts region (the composition tail) beyond the storage step, and Phase-1 init side effects (for example storage dirs) happen before the refusal. The pin needs at least 4 production boots of the built CLI (about 5 s each), so it needs a declared measured budget against the 10 s per-file line.",
    "B: one loud line at the storage step, with no refusal and conditional wording ('unless a plugin of your own registers a driver ...'). Business need: the remedy reaches the operator first, and nothing that boots today changes. Long-term fit: a boot that cannot succeed still runs to the misleading downstream failure, against rule 3 'prefer failing to falling back'. AI-proofing: weakest. Startup focus: smallest (S). It does not meet the card's acceptance: R1 still ends in the sys_organization error after the line, so triage's 'pin it so the downstream error never appears' would have to be narrowed.",
    "C: the storage-step refusal as ruled, lifting the 'never refuse a configuration that boots today' guard. R9, R10 and R11 would be refused too. Business need: in-repo producers of those three shapes are zero. Every in-repo driver provider (DriverPlugin as com.objectstack.driver.X, and DefaultDatasourcePlugin) matches the name heuristic, and no in-repo invocation uses --no-server --preset minimal or an onEnable driver. Third parties are unknown. Long-term fit and AI-proofing: strongest if paired with a declared-provider rule (for example hasStorageDriverPlugin honouring a providesServices entry starting driver.). That lives in packages/cli/src/utils/stack-collections.ts or core, outside this claim, and still breaks today's undeclared providers at once (no staged window). Startup focus: S for the refusal; the declared-provider half is its own card.",
    "E: converge the host-config branch onto the shared default-database resolution instead of refusing. This is outside triage's 'refusal or one loud line' scope. The standalone serve path (R14) and os start already resolve production storage through @objectstack/runtime resolve-project-database.ts, whose header records the ruling 'One shared resolution function ... commands MUST NOT carry their own fallback filename': explicit URL, then environment, then the config's declared default datasource, then STATE_DIR/data/objectstack.db. Only the host-config storage step answers 'prod none' (storage-driver.ts: 'Production returns null so a missing driver surfaces loudly downstream'). Business need: every production door would then agree for the same project. Long-term fit: it removes a fork. AI-proofing: no trap left, but it is a fallback rather than a refusal. Startup focus: S to M, storage step only. It reverses the seat's REFUSE ruling and rule 3's preference, so it needs triage or the maintainer."
    ],
    "recommendation": "A, because it is the only option that meets both halves of the seat's ruling as written on every measured shape: it refuses what fails anyway (R1, R2, R4-R8) and never refuses what boots today (R9-R11), before any plugin starts. Business need: it fixes the card's reproduction. Long-term fit: it judges registered drivers, not names. AI-proofing: the refusal is loud and names the remedy. Startup focus: no new gate and no contract change. The price is the declared-nowhere 'auth needs a driver at start' predicate (the PM's hypothesis (b) already named 'the auth tier' as the signal) and an M size. If the seat prefers fixing the producer over refusing, E is the coherent alternative, but it needs a scope change from triage."
    }
    ],
    "out_of_scope_findings": [
    "carrier: #23573 · objectql's sys_metadata hydration ERROR ('Fix: check the datasource behind sys_metadata - connection, credentials, and that the table exists') still prints on every no-driver production path that options A, B and C leave unrefused. Measured on R3, R10 and R11, where the operator declared no datasource. The card's body already names this line, and packages/objectql is a hold-off on this claim. Option E removes it. · noted, not filed",
    "class: a · reach: public door objectstack serve objectstack.config.ts --no-server --preset minimal (production, R10) printed '-> API: http://localhost:33117/' and '-> MCP: http://localhost:33117/api/v1/mcp ... connect an AI client (Claude Code, Cursor, ...)' while no HTTP server was composed. A GET to that port failed with 'fetch failed' (nothing listening). The boot diagnostics in the same banner say 'RestApiPlugin: HTTP Server service http.server not found. REST routes skipped.' Landing: serve.ts ready banner (domain:cli). · dedupe words: no-server banner API URL; serve --no-server prints MCP connect URL; ready banner advertises unmounted server",
    "carrier: none · noted, not filed (read, not measured for migrate): the same project resolves production storage differently by door. os start, os migrate (resolveStandaloneDatabase) and standalone serve use the shared resolver (OS_HOME/data/objectstack.db), while host-config serve resolves none. This is the fork option E would close."
    ],
    "deviations": [
    "Stopped at the ruling's guard. The claim says 'if the measurement finds one that would be [refused], the dev stops and reports', and the dispatch says 'If you cannot draw that line from what the boot knows before plugins start, STOP'. The line cannot be drawn at the storage step. It can be drawn at the end of Phase 1 (option A), but that is a different mechanism, a second serve.ts region and an M size against the ruled S storage-step refusal, so I did not write it speculatively.",
    "The branch claude/issue-23573-serve-no-storage-loud is pushed at BASE 8d05456 with no commits (the write-route probe). The worktree was removed (git worktree remove, no --force: it held no edits).",
    "All measurement fixtures were throwaway projects in the scratchpad: @objectstack/spec, driver-sql and plugin-auth symlinked in; auth secrets were fixture literals; the processes were SIGTERMed by the probe. app-showcase was booted in place with no file changed. The probe script and logs are in the parent's scratchpad under issue-23573/ (probe.mjs, probe-*.log, repro-showcase-prod.log).",
    "The dispatch's gate list (dispatch-gates, typecheck, pnpm lint) was not run, because there is no diff to gate.",
    "Evidence hygiene: later probe runs reused a file name and overwrote the R2 and R3 logs. probe-hostObj-7b224f535f41.log now holds R12, and probe-hostObj.log holds a --preset minimal boot with no auth, which the existing no-auth gate refused. The R2 and R3 readings above are quoted from those runs' console output in this session."
    ],
    "files_changed": []
    }

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 14 (#23573, amended after the measurement 6184721596: a production host-config serve with no storage and a composed auth service is refused at the end of kernel Phase 1, before any plugin starts; one loud line where no auth is composed; nothing where a driver is registered) · 2026-10-15T06:11Z
    Session: session_01Q9PZ5Pzqcus4woVvTZwWNU
    Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23573-serve-no-storage-loud
    Worktree: objectstack-issue-23573
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    Scope: batch 3, by 「并发恢复3」. This amends claim 6184401635: same card, same branch, still one of the three in flight.
    Seat ruling on the dev's open question: option A.

    • What the readings showed. The ruled storage-step refusal would refuse three production shapes that boot today (R9-R11). The storage step cannot separate them from the failing ones, so the dev stopped, as ruled.
    • A meets both halves of the original ruling on every measured shape.
      • It refuses what fails anyway: R1, R2, R4-R8.
      • It never refuses what boots today: R9, R10, R11.
      • It is still before any plugin starts.
    • A follows the norms already in force:
      • AGENTS.md rule 3, "prefer failing to falling back";
      • storage-driver.ts's own design, "Production returns null so a missing driver surfaces loudly".
    • Not taken:
      • B (a line only): R1 still ends in the misleading sys_organization error.
      • C (refuse at the storage step): it refuses configurations that boot today.
      • E (fall back to the shared resolver's default file): it reverses rule 3 for this door, so it is a maintainer decision this seat does not take. It is noted, not filed: os start and host-config serve resolve production storage differently.
        File surface (read on origin/main; stop on a breach and explain it in the report):
    • packages/cli/src/commands/serve.ts:
    • One pin file beside the serve integration tests, with a declared measured budget if it exceeds the 10 s per-file line. One .changeset/23573-*.md.
    • ⛔ Hold-offs: not packages/objectql, not plugin-auth, not packages/core, not the drivers, not content/docs/**.
      Container & model: M, judged (two serve.ts regions, a Phase-1 guard plugin, production-boot pins), mode:subagent, model: default tier. Ceiling: Clause-②: no. A configuration that boots today is never refused.
      Clause-②: no
      Responsibility: serve's no-storage boot guard in packages/cli (this lane) | plugin-auth's need for a driver at start(), read as the predicate, not changed | every operator who runs a production host-config serve without OS_DATABASE_URL or a datasource
      Thread-read: 6184721596
      Serial constraints cleared:
    • PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469 is the one open PR touching serve.ts, at :3863, clear of both regions.
    • No open PR touches the composition tail.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23573,
    "status": "done",
    "branch": "claude/issue-23573-serve-no-storage-loud",
    "pr": "#23631",
    "session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the parent's session; this run is mode:subagent)",
    "premise_still_valid": true,
    "summary": "Option A is implemented as ruled in claim 6185741820, with two measured refinements. The serve.ts storage step only records productionResolvedNoStorage when resolveStorageDefinition answers null. One CLI plugin, com.objectstack.cli.no-storage-boot-guard, is registered last (after the seed-settlement announcer, immediately before runtime.start()) and only when that flag is set. Its init() reads ctx.getServices() once (driver.* keys, the map ObjectQLPlugin.start adopts from) plus the engine's getDefaultDriverName(). A driver present: silent. No driver, auth registered and tenancy.posture 'single': refused before any start(), exit 1, naming OS_DATABASE_URL, a storage driver plugin (new DriverPlugin(driver)) and --dev. No driver otherwise: one line naming the same fixes, and the boot goes on. Refinement 1: the refusal also requires tenancy.posture === 'single', because R20c (auth registered, group posture, no driver anywhere) boots today and AuthPlugin.start() reads sys_organization only under 'single'. Refinement 2: 'declare a datasource' is spelled 'mount a storage driver in the stack's plugins', because R4 (a declared default datasource with autoConnect) still fails: config datasources connect in AppPlugin.start(), after AuthPlugin.start(). Every shape that booted still boots (R9, R9e, R10, R11, R20c, R12, R13). Every measured sys_organization failure is refused with neither downstream line (R1 showcase, R2, R4, R8, R8b, R8c). PR #23631 is a draft assigned to sunhaolin. Its body reads back byte-identical with one session-URL footer.",
    "tests": "Final head 650e28a (merged with origin/main 7c6b7f4). The CLI closure was rebuilt first (turbo build --filter='@objectstack/cli...', 62 tasks, VERDICT command-exit 0). (1) The new pin test/serve-no-storage-boot-guard.integration.test.ts (5 production boots of bin/run.js with NODE_ENV unset: refusal; line before any start() with --no-server --preset minimal; a plugin registering driver.* in init() is silent; OS_DATABASE_URL control; --dev control) ran with 8 neighbouring serve tests (serve-config-boot-flat-docs, serve-config-boot-residual, serve-host-config-metadata-door, serve-migrate-exit-schema-parity, serve-view-container-read, serve-package-declared-capabilities, serve-runtime-state-project-key, serve-host-config): 'Test Files 9 passed (9) / Tests 61 passed (61)', VERDICT command-exit 0. The pin alone took 23.58 s, 24.37 s and 24.68 s over three runs. (2) pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: 'Test Files 290 passed (290) / Tests 4323 passed (4323)', VERDICT command-exit 0. The rest of the integration layer is declared to CI. (3) pnpm --filter @objectstack/cli typecheck: exit 0, 'check:test-typecheck: OK'. The pin is in the tsconfig.test.json program (tsc --listFilesOnly, 1 hit). (4) Ablation of the refusal, done on the committed fix through scripts/ablation-replace.mjs with its restore trap, before the merge, which touches no line of this diff. The condition got 'false &&' prefixed: anchor x1 -> x0, blob c016f7b009c1 -> 139994049e2d. After the CLI build, ablation-dist-preflight found the marker in 1 built file. The pin read 'Tests 1 failed | 4 passed': the refusal leg failed on 'no refusal was printed', and the boot it read printed '[ObjectQL] No driver available for object sys_organization' and 'ERROR sys_metadata could NOT be read at boot'. Restore leg: blob == HEAD c016f7b009c1 and git diff HEAD empty. Then a rebuild and preflight --absent ('absent from all 632 built files', 'working tree clean against HEAD'), and the pin read 'Tests 5 passed (5)'. (5) Probe matrix with throwaway host-config fixtures and the built CLI. Before = base 9f81260 or report 6184721596 (8d05456); after = this branch, with R2, R3, R9, R10, R11 and R20c re-measured on 650e28a. R1 showcase+secret: refused, 0 downstream lines. R2: refused, the fixture's init marker present and its start marker absent. R3 (no secret): the line, then the same package-service failure as before. R4, R8, R8b, R8c: failed before, refused now. R9, R9e: boot, silent. R10, R11, R20c: boot before; line + boot now. R12 OS_DATABASE_URL and R13 --dev: boot, silent.",
    "gates": "Local, on 650e28a. (a) node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 86 commands (19 of them from the scripts/check-cli-test-child-env.mjs edit). All 86 were run with exit codes captured before any pipe: 86 exit 0. --ran over the 'CMD :: exit N' record: '86 derived famil(ies) accounted for, 86 run, 0 NOT-MEASURED (a DERIVED zero)', exit 0. An earlier 67-command run on the pre-merge head had two reds, both since resolved. check:cli-test-child-env exited 1 on two self-test pins: the it.each env, and the new bin/run.js spawner missing from the pinned population. That was fixed by literal env legs and the roster entry. check:dual-build-cjs-loads exited 3 with PREREQUISITE NOT MET. Both are green in the final run. (b) The edited gate's own suite: node scripts/check-cli-test-child-env.mjs --self-test ('152 cases pass') and its audit, both exit 0. node scripts/check-ratchet-remedy-authority.mjs and its --self-test, which name it, exit 0. (c) Read though not derived: pnpm check:startup-registry-verdict exit 0, pnpm check:init-service-contract exit 0. (d) Lint narrowed per the lane order: eslint --no-inline-config --format json over serve.ts, the pin and check-cli-test-child-env.mjs, exit 0, 0 messages. Three pieces of evidence: the root lint script is 'eslint . --no-inline-config' (same flags); eslint.config.mjs enables no type-aware linting for any file, so verdicts are per file; --print-config resolves active rules for all three files (5, 5, 2), so none is ignored. Repo-wide pnpm lint is CI's. (e) Local-only families check:dev-prereqs and check:skill-frame-freshness are not owed: no packages/spec build input and no four-axis frame file is in the diff. (f) CI on PR #23631 head 650e28a, read once at open: 23 check-runs, 21 in_progress, 1 success, 1 skipped. The required five (Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard) are in_progress. Not waited on, per the role.",
    "mcp_calls": "0",
    "api_writes": "2 relay strokes, both through scripts/pm/with-fleet.sh --via dispatch (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft) then POST /repos//issues/23631/assignees ['sunhaolin']; read back 11736 bytes sent, 11736 stored. (2) This os-dev-report: POST /repos//issues/23573/comments. 0 label writes: the dispatch named none, and skip-changeset does not apply because a changeset ships. Not REST: git push of the branch, 7 pushes, all fast-forward with no force. The first moved the empty branch 8d05456 -> 9f81260 as the write probe; the last pushed the merge 650e28a. Reads: GET issue 23573 and its comments, GET pulls/23631, GET check-runs.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: public door OS_DATABASE_URL=redis://127.0.0.1:6399/0 objectstack serve objectstack.config.ts --dev (host fixture, auth secret, at 650e28a) printed '✓ Server is ready' with banner 'Driver: SqlDriver(better-sqlite3) → :memory:'. No line says the URL was ignored, so the operator's named database is silently replaced by an in-memory store. · evidence: inferDriverTypeFromUrl (packages/cli/src/utils/storage-driver.ts) answers '' for an unrecognized scheme, and resolveStorageDefinition falls through to the dev ':memory:' default. An unrecognized OS_DATABASE_DRIVER is refused there ('Booting on the SQLite default instead would silently ignore the driver you asked for'), but the URL arm has no such refusal. In production the same URL now reaches this PR's no-storage refusal ('no recognized OS_DATABASE_URL'), so the silent half is dev only. Landing: domain:cli. · dedupe words: unrecognized OS_DATABASE_URL scheme ignored; serve --dev unknown database URL falls back to memory; inferDriverTypeFromUrl empty string silent SQLite default",
    "class: a · reach: public door objectstack serve objectstack.config.ts (production) of a host config declaring datasources [{ name: 'default', driver: 'sqlite', config: { filename }, autoConnect: true }] with OS_AUTH_SECRET set. Base 9f81260: exit 1, 'No driver available for object sys_organization' (R4). With this PR it is refused by the no-storage guard. The declared default datasource never serves the boot, because AppPlugin.start() connects config datasources after AuthPlugin.start() needs a driver (debug start order measured in 6184721596). · evidence: probe-base-R4 and probe-new-R4 logs in the PM scratchpad issue-23573/b. Landing: packages/runtime AppPlugin datasource connect timing (domain owner to route). · dedupe words: host config declared default datasource connects after auth start; datasources autoConnect default production serve sys_organization; AppPlugin start connects datasources too late",
    "class: a · reach: public door objectstack serve objectstack.config.ts --no-server --preset minimal (production, no storage, R10 at 650e28a), and likewise R3, R11 and R20c. objectql prints 'ERROR sys_metadata could NOT be read at boot ... Fix: check the datasource behind sys_metadata - connection, credentials, and that the table exists - then restart.' No datasource was declared and no driver is registered. This PR's '⚠ No storage:' line now precedes it with the real fixes, but the ERROR's own remedy still names a datasource nobody declared. 6184721596 carried this on #23573, which this PR closes, so it needs its own card. Landing: packages/objectql/src/plugin.ts hydration ERROR (a hold-off on this claim). · dedupe words: sys_metadata hydration error remedy no driver; objectql DB hydration skipped no datasource declared; misleading fix datasource behind sys_metadata",
    "carrier: none (承接者:无) · noted, not filed: R3 (production host config, no auth secret, default preset) still dies at package-service start. Its error 'Execute requires options.object to select a driver, or a default driver to be configured. Configure datasourceMapping with default: true ...' now follows the new line with the real fixes. Refusing it was outside the ruling's predicate. In the PR's Acceptance notes."
    ],
    "deviations": [
    "Refusal predicate refined from 'auth service registered' to 'auth registered AND tenancy.posture === single'. R20c boots today with auth registered and no driver anywhere: stack AuthPlugin({ membershipPolicy: 'invite-only' }) + OrganizationsPlugin, OS_TENANCY_POSTURE=group, OS_PLATFORM_OWNER_EMAIL, --no-server --preset minimal. The ruled predicate would have refused it, against the claim's 'a configuration that boots today is never refused'. AuthPlugin.start() reads sys_organization only when this.tenancy.posture === 'single' (auth-plugin.ts start()). R16-R21 were probed to find R20c.",
    "Remedy 'declare a datasource' is spelled 'mount a storage driver in the stack's plugins (e.g. new DriverPlugin(driver) from @objectstack/runtime)'. R4 declares a default datasource with autoConnect and still fails, because config datasources connect after AuthPlugin.start(). That defect is filed above as out-of-scope.",
    "File surface: scripts/check-cli-test-child-env.mjs (+7/-1). It pins the exact set of test files that spawn bin/run.js and requires a file joining it to be named in the PR. The roster entry has its reason, and 'seven' became 'eight'. The ordered pin (production boots of the built CLI) cannot avoid joining that population. Not a hold-off, and not a governed surface. Declared here as a breach of the claim's listed surface.",
    "The pin is named .integration.test.ts, not .e2e.test.ts. The .e2e name moves a file to the nightly run on main (OS_TEST_TIERS, scripts/nightly-tiers.mjs), and this pin guards a boot order any serve.ts edit can break. Its budget is a header line, '// test-budget: 40 — spawns the real producer once per single-cause fixture: …' (ruling record 6181973363, read back through readTestBudget): the slower of two local readings, 24.4 s on a shared container, x 1.25, rounded up to 40. No CI reading exists yet.",
    "The no-auth line is worded as of the end of plugin init ('… so objects have no store unless a plugin registers one when it starts'), not as a verdict. A start() can still register a driver: R11's onEnable runs in AppPlugin.start() after the guard's init(). AGENTS.md 'Startup registry reads' governs this. R11 therefore gets the line and boots, as hypothesis (b) expected.",
    "Hypotheses (a) and (b) hold: both are read from the kernel registry, with no CLI-side auth flag. 'auth' and 'tenancy' are direct registerService entries made unconditionally in AuthPlugin.init(). Drivers are the 'driver.' keys plus the engine's getDefaultDriverName(). Hypothesis (c): a refused boot left no disk side effects (no .objectstack in the fixture or in examples/app-showcase, OS_HOME empty). The only Phase-1 output is two init() log lines: the SDUI-manifest notice and the StorageServicePlugin local-driver warning.",
    "Conflict noted. The dispatch's CLI lane always adds 'pnpm lint', while the role file says repo-wide scans are CI's and never owed. I took the dispatch's own alternative, the proven narrowing with three pieces of evidence (gates d).",
    "The harness's attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. Commits carry the AGENTS.md model-free pair (Claude-Session + Co-authored-by: Claude), and the PR body ends with the session-URL footer, because AGENTS.md takes precedence. The merge commit 650e28a carries git's default message and no trailers.",
    "The empty branch was fast-forwarded from 8d05456 to origin/main 9f81260 by the first push, with no force. origin/main 7c6b7f4 was merged before the PR opened (AGENTS §10). It brought PR #23469's 5c-ter OrganizationOwnershipBootGuardPlugin and objectql changes. The probe matrix rows marked * and the whole test and gate set were re-run after it.",
    "Cleanup is the step after this comment: rm -rf the worktree's node_modules, then git worktree remove ../objectstack-issue-23573 with no --force. Every commit is pushed and the branch head equals the remote's 650e28a.",
    "Extra builds for measurement only: @objectstack/organizations, and the examples/app-showcase closure for R1. Nothing from them is committed. Probe fixtures and logs are in the PM scratchpad, issue-23573/b (probe2.mjs, probe-
    .log). Logs are tagged per run, so none is overwritten."
    ],
    "files_changed": [
    ".changeset/23573-serve-no-storage-loud.md",
    "packages/cli/src/commands/serve.ts",
    "packages/cli/test/serve-no-storage-boot-guard.integration.test.ts",
    "scripts/check-cli-test-child-env.mjs"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23631 comment 6187623746 · 2026-10-15T08:14Z


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23631 → edac2183c0 · 2026-10-15T08:55Z

    domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU · holder of amended claim 6185741820, released by this landing. Review: the seat record 6187623746 on the PR (pointer 6187636323 on this card). No contract review was owed.

    Reading Value
    PR merged: true, merged_at 2026-10-15T08:54Z, through the merge queue (added_to_merge_queue 2026-10-15T08:18Z)
    Queue branch gh-readonly-queue/main/pr-23631-* absent (git ls-remote, 0 refs)
    origin/main edac2183c0 is an ancestor; one parent (b513343ac1); 4 files, +403 / −1, as reviewed at 650e28a731
    Closing keywords in the landed commit Fixes #23573 only
    Lane reconciliation 13 open domain:cli cards (read at this stamp), #23573 no longer among them

    State change: #23573 closed completed by the merge; pm:dispatched removed in this act.

    Follow-ups, filed from the build report:

    Option E (host-config serve falling back to the shared resolver's default file) was not taken; it is the maintainer's to rule.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions