You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec: the stack's datasources accepts a datasource named default, which every boot refuses as host-reserved — defineStack / os validate / publish pass a config that boots in no posture #23634
Filing gate ①, class (a). reach:measured at the public doorobjectstack serve objectstack.config.ts (production), on a host config declaring datasources: [{ name: 'default', driver: 'sqlite', config: { filename }, autoConnect: true }] with OS_AUTH_SECRET set (boot R4).
Filed by domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, from #23573's build report (os-dev-report on #23573, PR #23631, head 650e28a731). ⛔ Not a claim. Grade and lane are triage's.
What was measured
Base 9f81260487: exit 1, No driver available for object 'sys_organization'.
Why: the declared default datasource never serves the boot. AppPlugin.start() connects config datasources after AuthPlugin.start() needs a driver. The debug start order was measured in 6184721596: objectql, hono, metadata, …, settings, auth, then AppPlugin.
Evidence: the probe-base-R4 and probe-new-R4 logs in the seat's scratchpad, under issue-23573/b.
Reader who acts
Triage grades it and routes it. The landing is the runtime's datasource connect timing (AppPlugin, packages/runtime).
Read fresh. A host config that declares datasources: [{ name: 'default', …, autoConnect: true }] cannot boot a production serve. This was measured at the public door (boot R4).
Base: exit 1, No driver available for object 'sys_organization'.
Cause:AppPlugin.start() connects config datasources after AuthPlugin.start() already needs a driver (start order measured in 6184721596).
Grade p2: a declared config shape that never serves the boot it declares. The operator gets a refusal or a crash, not silence. Other storage routes work: a driver plugin, OS_DATABASE_URL, --dev.
Lane domain:cli (packages/runtime, AppPlugin, by the lane table).
Order: none. PR #23631 merged at 08:54Z (edac2183c0). When the declared default serves the boot, this card's PR restores "declare a default datasource" to that PR's no-storage remedy line in serve.ts.
Scope:
A declared default datasource is connected before any plugin's start needs a driver.
Pin a production boot over a declared SQLite default.
Claim: PM loop round 16 (#23634: a host config's declared default datasource (autoConnect) is connected before any plugin's start() needs a driver, so a production serve over it boots; serve's no-storage remedy names "declare a default datasource" again) · 2026-10-15T09:07Z
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-23634-declared-datasource-boot-order
Worktree: objectstack-issue-23634
Domain: domain:cli
Seat: domain:cli#2 (seat post #22648)
Scope: batch 3, by 「并发恢复3」. This is the third slot, beside #23558 and #23546.
Order, read at this act: after the lane's p1 #23546 (claimed in this round) and the target:v18#23558 (in flight), this is the lane's oldest dispatchable p2 bug. #23531's remainder waits on #23586, #23497 is pm:blocked on #23632, and #23050 is seat 1's. Triage 6188422721 named no order condition. PR #23631, whose remedy line this card restores, merged at 08:54Z.
File surface (read on origin/main; stop on a breach and explain it in the report):
packages/runtime/src/app-plugin.ts: where config datasources are connected (today in start(), after AuthPlugin.start() needs a driver). Only the earliest point at which a declared default datasource (autoConnect) is connected and registered as a driver.
⛔ Triage's line: the fix does not reorder plugins by name.
packages/cli/src/commands/serve.ts: only the no-storage guard's noStorageRemedies list (:5143-:5147) and the refusal's one-line Error text (:5170), to name "declare a default datasource" again once it works. ⛔ No other step.
If the only fix is a plugin-order or contract change outside this surface, the dev stops and reports.
Clause-②: no
Responsibility: the declared-datasource connect timing in packages/runtime AppPlugin (this lane) and serve's remedy line | plugin-auth's driver need at start(), read | every operator whose host config declares its default datasource rather than OS_DATABASE_URL or a driver plugin
Thread-read: 6188422721
Serial constraints cleared:
No open PR touches app-plugin.ts or serve.ts (read at this act).
os-dev-report
{
"issue": 23634,
"status": "needs_decision",
"branch": "claude/issue-23634-declared-datasource-boot-order",
"pr": null,
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the parent's session; this run is mode:subagent)",
"premise_still_valid": false,
"summary": "The card's symptom is real, but its mechanism is not why the declared default never serves. R4 declares datasources: [{ name: 'default', ... autoConnect: true }], and AppPlugin.start() refuses a stack-declared datasource named default in every posture: with OS_DATABASE_URL (M2), with --dev (M3) and on the standalone path (M4). The refusal reads "declares a datasource named 'default' - that name is reserved for the host's primary datasource" (app-plugin.ts:1120-1126). It is documented in drivers.mdx:265 ("The name default is host-reserved: an app bundle declaring a datasource with that name is rejected at load") and enforced again at the admin door (datasource-admin-service.ts:1091). So connecting it earlier only swaps PR #23631's refusal (M1) for the reserved-name refusal (M2). For any other name, hypothesis (b)'s init-time connect would change which datasource a config that boots today resolves to. The connect would land before ObjectQLPlugin.start() adopts a stack plugin's driver.*, and the engine makes its first-registered driver the default (engine.ts:8327, isDefault || this.drivers.size === 1). M9 boots today with default com.objectstack.driver.sql; that config would move every unbound object, platform objects included, onto archive. Both fixes are a contract change or a resolution change, so I stopped per the ruling: no code, no PR, and serve.ts's remedy line is left as PR #23631 wrote it, because the condition for restoring it ("once the declared default serves the boot") is not met. The options are in open_questions.",
"tests": "No test was written (no code). Measurement only. Tree: objectstack-ai/objectstack at 7dc7d22 (BASE, origin/main; PR #23631 = edac218 is in it). Build: os-verify-lock.sh -c \"NODE_OPTIONS=--max-old-space-size=4096 pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2\": Tasks 61 successful, 61 total; VERDICT command-exit 0. The first attempt was VERDICT queue-timeout (exit 99) and is NOT MEASURED. Each boot: node packages/cli/bin/run.js serve objectstack.config.ts -p PORT on the built CLI. NODE_ENV was unset (production) and OS_CLOUD_URL=off. OS_DATABASE_URL was unset unless noted. OS_AUTH_SECRET was a fixture literal. Fixtures are #23573's probe2.mjs host fixtures plus 5 variants (probe3.mjs, in the parent's scratchpad under issue-23634/p). Results: M1 R4 (name: 'default', sqlite, autoConnect), production: exit 1, PR #23631's refusal 'Refusing to boot: this production serve has no storage ...'. M2 R4 + OS_DATABASE_URL=file:ctl.db: exit 1; '[fx] host-marker start' printed, then 'Plugin plugin.app.com.example.fx failed to start - rollback complete: [AppPlugin] app com.example.fx declares a datasource named default - that name is reserved for the host's primary datasource. Rename it (e.g. fx_primary) and route objects to it explicitly, or omit it to use the host default.' M3 R4 + --dev: exit 1, the same refusal. M4 the same default on a non-host config (standalone path), production: exit 1, the same refusal. V1 os validate objectstack.config.ts on R4, with sharingModel: 'private' added to clear the unrelated security-owd-unset rule: exit 0, 'Data: 1 Objects 1 Fields 1 Datasources'. M5 a host config with datasourceMapping: [{ default: true, datasource: 'primary' }] over a declared sqlite primary (the shape resolve-project-database.ts treats as the config-declared default), production: exit 1, PR #23631's refusal. M6 M5 with autoConnect: true on primary: exit 1, the same. M7 M5 + OS_DATABASE_URL=file:ctl.db: 'Server is ready', health 200, data 401, then 59 'DATABASE_ERROR ... no such table' lines on platform objects (sys_metadata 10, sys_setting 7, sys_platform_setting 7, sys_user_permission_set 6, sys_migration 6, sys_organization 2, and more). Opened read-only afterwards: ctl.db held 72 tables and 1 sys_organization row; ds-primary.db held 13 tables and 'no such table: sys_organization'. M8 M5 + --dev: ready, health 200, 61 DATABASE_ERROR lines. M9 R9 (plugin com.acme.store registers a SqlDriver as driver.* in init()) plus a declared archive with autoConnect, production, --log-level debug: ready, health 200. The log read 'Registered driver com.objectstack.driver.sql', then 'Set default driver com.objectstack.driver.sql' (ObjectQLPlugin.start adoption, plugin.ts:965), then 'Registered driver archive' and 'Auto-connected declared datasources [archive]' (AppPlugin.start). No ablation: nothing was changed.",
"gates": "None owed: the branch has no diff. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack exited 2 with 'this branch changes nothing against origin/main (merge base 7dc7d22) - nothing to derive' (the repo assertion held). No typecheck, no pnpm lint and no ablation, because no file was edited. No PR, so there are no check-runs.",
"mcp_calls": "0",
"api_writes": "1 relay stroke: scripts/pm/post-stamped.mjs, which sends POST /repos//issues/23634/comments (this os-dev-report) as objectstack-fleet[bot] through the fleet-write relay. Also one git push of the empty branch claude/issue-23634-declared-datasource-boot-order at BASE 7dc7d22 (the write-route probe; not REST). Reads (session identity, GET only): issues/23634, its /comments, issues/comments/6184721596 and pulls/23631. No PR, no label write, no assignee write.",
"open_questions": [
{
"question": "What is 'a host config's declared default datasource', and should it serve a bare production serve? The R4 spelling (datasources[].name === 'default') is refused by the host-reserved contract in every posture (M2-M4), and os validate passes it (V1). The spelling the shared resolver recognises (datasourceMapping { default: true, datasource: NAME }, resolve-project-database.ts readConfigDeclaredDefault, maintainer ruling recorded in its header) is honoured by os start / os migrate / standalone serve, but not by host-config serve's storage step. Moving AppPlugin's connect into init() (hypothesis b) fixes neither: R4 is still refused at start, and for any other name it changes M9's default driver.",
"options": [
"A: Keep the reservation, and refuse default at authoring instead of after auth starts. The stack's datasources collection refuses a datasource named default. The refusal goes on that collection, not on DatasourceSchema, whose documents include the host's own default row. defineStack, os validate and publish then reject R4 with the remedy (set OS_DATABASE_URL, or declare a datasource under its own name and route objects to it). The published skill line skills/objectstack-data/rules/datasources.md:5 ('Most apps need only the default datasource; declare more ...') is reworded so it no longer reads as an invitation to declare it. serve's remedy line stays as PR #23631 wrote it, and this card's runtime half closes as 'not declarable'. Business need: measured in-repo producers of R4 are zero (examples, create-objectstack, skills, docs). Its only measured producer is the agent-written fixture R4 itself. Every in-repo production entry goes through os start, which hands serve an OS_DATABASE_URL (read in 6184721596). Long-term fit: one contract is kept: default is host-owned (drivers.mdx:265, admin door :1091), and the shared resolver already declines a default-named rule (resolve-project-database.ts:224). The refusal moves from Phase 2 to authoring, which is contract-first. AI-proofing: strongest. The natural AI spelling is refused at os validate with its remedy, where today it passes there (V1) and dies after auth starts (M2). Startup focus: S to M, and no capability expansion. It is a contract tightening, not a new CI gate. Surface: packages/spec (the stack schema) plus the skill line, both outside this claim. Changes no config that boots today: R4 boots in no posture.",
"B: The host config's own default becomes the host's primary when the host declared none. When a production host-config serve's storage step resolves nothing, serve hands the config's default entry to DefaultDatasourcePlugin, the existing Phase-1 connect (asDefault, bootCritical). AppPlugin then skips that entry instead of refusing it. With OS_DATABASE_URL or --dev, the reservation keeps refusing it, as today (M2, M3). Business need: the same zero producers, and no named operator. Long-term fit: it adds a second spelling of 'the project's default database' beside the mapping rule, and os start / os migrate keep ignoring it, which reopens the per-door fork the shared resolver closed. AppPlugin cannot decide 'the host declared no default' in its own init(). DefaultDatasourcePlugin precedes it only by insertion order, and a stack plugin's driver arrives later, so per AGENTS.md 'Startup registry reads' serve has to pass AppPlugin an explicit signal. AI-proofing: weak. The same declaration boots in one posture and is refused in two. Startup focus: M. It touches serve.ts's storage step (outside this claim's serve.ts surface), the app-plugin.ts reservation and the drivers.mdx contract text. Changes no config that boots today.",
"C: Host-config serve honours the mapping-rule declared default, through the shared resolver. In production, when the storage step resolves nothing, serve consults the same datasourceMapping { default: true } plus datasources[] resolution that os start uses (today artifact-only; it needs a config-object entry point in packages/runtime) and hands the result to DefaultDatasourcePlugin. The remedy line then names that rule. R4 stays refused, and pairs with A's authoring half. Business need: zero in-repo producers of the mapping shape. examples/app-crm deleted its rule, and its config comment records why. Long-term fit: best convergence, since every door resolves one project's default the same way (option E in 6184721596). AI-proofing: medium. The mapping spelling is less natural, so R4's spelling still needs A. Startup focus: M, and a capability expansion with no measured pull. It lands outside this claim (runtime resolve-project-database.ts plus serve.ts's storage step). A production no-URL host config that boots today on a stack or onEnable driver and also carries a mapping default would gain a second default, so its precedence needs ruling. It inherits M7's split brain unless the rule is installed before boot writes, and that is objectql's, a hold-off.",
"D (the dispatched hypothesis b, for the record): AppPlugin.init() connects autoConnect declared datasources before any start(). This is rejected on measurement. R4 is still refused at start (M2-M4). For any other name, the init-time connect precedes ObjectQLPlugin.start's driver.* adoption, and engine.ts:8327 makes it the default. M9 boots today on com.objectstack.driver.sql; under D it would move to archive, platform objects included. That changes an existing config's resolution, which the ruling forbids."
],
"recommendation": "A, on all four axes. Business need: neither declared-default spelling has an in-repo producer, the only R4 producer measured is an agent-written fixture, and every in-repo production entry already supplies OS_DATABASE_URL through os start. Long-term fit: it keeps one contract (default is host-owned) and the one shared resolution, where B forks both. AI-proofing: it turns a late Phase-2 refusal into an authoring refusal with the remedy, at the exact door that passes the shape today (V1). Startup focus: it expands no capability. It also leaves PR #23631's remedy line correct as written, since that line deliberately does not send an R4 operator to the thing they already did. C is the coherent route only if a named operator needs a bare production serve over a config-declared default; it then needs triage scope beyond this claim, and objectql's timing fix first (M7). Either way this card's claimed surface (app-plugin.ts connect point plus serve.ts remedy text) cannot deliver the scope as written."
}
],
"out_of_scope_findings": [
"class: a · reach: public door objectstack serve objectstack.config.ts (production, OS_DATABASE_URL=file:ctl.db; also --dev) on a host config declaring datasourceMapping: [{ default: true, datasource: 'primary' }] over a declared sqlite primary (M7, M8): 'Server is ready' and health 200, then 59 (URL) / 61 (--dev) DATABASE_ERROR ... no such table lines on platform objects (sys_metadata, sys_setting, sys_organization, ...). The URL store holds 72 tables and the Default Organization; the mapped primary holds 13 tables and no sys_organization. Read in code, not measured: AppPlugin.start() installs the mapping (ql.setDatasourceMapping), and that runs after ObjectQLPlugin.start's schema sync and AuthPlugin.start's Default Organization, both of which used the default store. After it, every unbound object routes to a datasource whose schema never synced. The routing order is objectql's (resolveDatasourceBinding, step 2 before step 5). Landing: objectql or runtime boot order; objectql is #23636's hold-off. Producer: none in-repo (examples/app-crm deleted its rule). · dedupe words: datasourceMapping default rule platform objects no such table; mapping installed after schema sync split brain; sys_organization routed to mapped datasource after boot",
"class: c · reach: os validate objectstack.config.ts exit 0 ('1 Datasources', V1) on a config declaring datasources: [{ name: 'default', ... }], which every serve posture refuses at AppPlugin.start (M2-M4). Producer: the R4 fixture, written by an agent for #23573's measurement and then read by this card as 'a declared default datasource'. Likely seed: skills/objectstack-data/rules/datasources.md:5 ('Most apps need only the default datasource; declare more ...'). This is option A's subject: if A is chosen, the seat folds it into that card rather than filing it separately. · dedupe words: datasource named default reserved validate passes; host-reserved default datasource authoring refusal; os validate default datasource AppPlugin refuses at start",
"carrier: none · noted, not filed (read, not measured beyond M9): engine.ts:8327 makes the first driver to reach registerDriver the engine default, so which driver serves unbound objects (platform objects included) depends on Phase order rather than on any declaration whenever no driver is registered asDefault. Measured consequence today: none wrong (M9 resolves to the stack plugin's driver). It is the reason hypothesis (b) changes resolution."
],
"deviations": [
"Stopped at the ruling's guard ('If a fix would change which datasource an existing config resolves to, STOP and report needs_decision'; 'If the only fix is a plugin-order or contract change outside this surface, STOP and report'). No code, no PR, no changeset, and serve.ts's remedy line was not touched: the ruling restores it only 'once the declared default serves the boot', and no in-surface fix makes it serve.",
"premise_still_valid is false for the card's stated cause, not for its symptom. R4 never serves (true). The cause is the host-reserved-name refusal at AppPlugin.start, not only the connect timing: with a driver present (M2, M3) the same declaration is refused.",
"The branch claude/issue-23634-declared-datasource-boot-order is pushed at BASE 7dc7d22 with no commits (the write-route probe). The worktree ../objectstack-issue-23634 is removed after this report (git worktree remove, no --force); it holds no edits.",
"R9-R13 from #23573 were not re-measured: nothing changed, so nothing can regress. M9 is a new boots-today shape, measured because it decides hypothesis (b).",
"All fixtures are throwaway projects in the parent's scratchpad (issue-23634/p): @objectstack/spec, driver-sql, plugin-auth, objectql and organizations are symlinked in, and auth secrets are fixture literals. The probe SIGTERMs each process it starts. The V1 copy (vfx) differs from the R4 fixture only by sharingModel: 'private' (grep count 1).",
"The dispatch's gate list (dispatch-gates list, typecheck, pnpm lint, ablation) was not run: there is no diff to gate (dispatch-gates exit 2)."
],
"files_changed": []
}
Release: domain:cli seat 2 · sunhaolin · session_01Q9PZ5Pzqcus4woVvTZwWNU · 2026-10-15T09:41Z · cause: the card's premise is false (this seat's filing error), and every fix is a contract or resolution change outside the claim · destination: pm:queue + pm:retriage, unassigned
The readings dev ran under claim 6188533413 and stopped at the ruling's guard (os-dev-report on this card). No code, no PR.
This seat's correction, in public. This card was filed from #23573's out-of-scope finding, and it named the wrong cause.
What the card said: R4's declared default datasource connects after AuthPlugin.start().
What is true: R4 declares datasources: [{ name: 'default', … }]. AppPlugin.start() refuses a stack-declared datasource named default in EVERY posture, because the name is reserved for the host's primary: "declares a datasource named 'default' — that name is reserved for the host's primary datasource" (app-plugin.ts:1120-:1126). It is documented at drivers.mdx:265 and enforced again at the admin door (datasource-admin-service.ts:1091).
For any other name, an init-time connect would change which datasource a config that boots today resolves to, because the engine makes its first-registered driver the default (engine.ts:8327). M9 boots today on com.objectstack.driver.sql and would move every unbound object, platform objects included, onto archive.
R4 was an agent-written measurement fixture. No in-repo producer was found: not in the examples, create-objectstack, the skills or the docs.
The options the readings name (four-axis analysis in the report's open_questions):
A, recommended by the dev and this seat: keep the reservation and refuse default at authoring.
The stack's datasources collection refuses that name, so defineStack, os validate and publish reject R4 with its remedy. Today os validate passes it (V1).
Reword skills/objectstack-data/rules/datasources.md:5 ("Most apps need only the default datasource; declare more …").
This lands in packages/spec and the skill line, outside domain:cli, and it is a contract tightening of an authoring shape that boots in no posture.
B: the host config's own default becomes the primary when the host declared none. This adds a second spelling of the project default, and doors would fork again.
C: host-config serve honours the shared resolver's mapping-rule default (datasourceMapping { default: true }). It is the best convergence, but there is no measured pull, and it inherits the split-brain finding below.
D (rejected on measurement): connect in AppPlugin.init().
PR #23631's remedy line stays as written. Its condition to change ("once the declared default serves the boot") is not met, and it deliberately does not send an R4 operator to the thing they already did.
Filed from the same readings: the datasourceMapping default split brain (M7, M8) as its own card. It is linked below once filed.
Asked of triage: re-route this card per option A (lane domain:spec for the stack schema, plus the skill line), or close it in favour of a new A card, or rule otherwise.
changed the title [-]runtime: a host config's declared default datasource (autoConnect) never serves a production serve's boot — AppPlugin.start connects config datasources after AuthPlugin.start already needed a driver for sys_organization[/-][+]spec: the stack's `datasources` accepts a datasource named `default`, which every boot refuses as host-reserved — `defineStack` / `os validate` / publish pass a config that boots in no posture[/+]on Oct 15, 2026
Claim: PM loop round 7 (second shift, parallel again) · 2026-10-15T22:11Z · triage's proxy ruling A (6189356908): the stack's datasources collection refuses a datasource named default, which every boot refuses as host-reserved. So defineStack, os validate and publish say the runtime's sentence and remedy at authoring time. One PR, Fixes #23634.
Session: session_01LFqdRsuM5nkqoJdHd5ztVk (the seat; the dev is a cloud session whose draft PR carries its own session link)
Account: os-bill (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-23634-stack-datasource-default-refused
Worktree: objectstack-issue-23634
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface (at origin/main9bb93706f or later; stop on a breach and explain it in the report):
packages/spec/src/stack.zod.ts: the datasources collection only, with the refusal and its remedy (set OS_DATABASE_URL, or declare the datasource under another name and route objects to it).
Pins: a packages/spec test for defineStack, plus the os validate pin where the CLI's validate pins live. The ruling's two CONTROLS go beside them: a datasource named fx_primary passes, and the host's own default row still parses through DatasourceSchema.
A semantic migration entry and the generated outputs, if the repo's ADR-0087 gates ask for them for this narrowing. One changeset, .changeset/23634-*.md.
⛔ Not packages/spec/src/data/datasource.zod.ts (DatasourceSchema documents the host's own default row). ⛔ Not packages/cli/src/commands/serve.ts: PR fix(cli): a production serve with no storage says so before any plugin starts, and refuses when its auth service needs a driver #23631's remedy line stays as written. ⛔ Not skills/**: if skills/objectstack-data/rules/datasources.md invites declaring default, the report gives the line and the rewording, for the skills lane.
Container & model: M, mode:cloud, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Cloud by the seat's interim rule ([Decision] Three spec contract dev agents in one container queued ~70 min each on the shared verify lock: leave package test:repo to CI, and send build-heavy cards to cloud sessions? #23607): packages/spec plus the CLI's validate pins is a build-heavy closure. Review ceiling CONTRACT_REVIEW_TIER, reason: packages/spec's published accept set narrows.
Clause-②: no
Responsibility: packages/spec's stack schema accepts a datasource named default that AppPlugin.start() refuses at every boot as host-reserved | none: os validate passes it (V1, report 6189075724); the refusal arrives only at boot | an author, human or AI, who declares a default datasource in a stack; no in-repo producer was found, and the one instance is an agent-written fixture
Thread-read: 6189356908
Ruling-ref: 6189356908 (triage's proxy ruling A, auto-adjudicated; not a decision card)
Serial constraints cleared:
os-dev-report
{
"issue": 23634,
"status": "done",
"branch": "claude/issue-23634-stack-datasource-default-refused",
"pr": "#23757",
"session": "session_01UvyD7nLUx7XGKKQVF37FY9",
"premise_still_valid": true,
"summary": "Ruling A is implemented. The stack's datasources collection (STACK_DEFINITION_COLLECTIONS_SHAPE in packages/spec/src/stack.zod.ts) carries a superRefine that refuses an entry named exactly default, at datasources.N.name with code custom. The text carries the runtime's sentence ("declares a datasource named 'default' — that name is reserved for the host's primary datasource") and the ruling's remedy (set OS_DATABASE_URL and omit the entry, or declare it under another name and route objects to it). DatasourceSchema, serve.ts and skills/** are untouched. defineStack (array and map form), the bare ObjectStackDefinitionSchema parse (the os validate door) and os validate --json are pinned, beside both CONTROLs (fx_primary passes; the host's own default row parses through DatasourceSchema). There is a D3 semantic entry stack-datasource-host-reserved-name-refused and a BREAKING-minor changeset with the registered disposition. The dropped-refinements ledger gains the 4 installed-package manifest.datasources sites (759 to 763), because the build requires it. The PM hypotheses were measured as follows. H1: the three sentences (app-plugin.ts, datasource-admin-service.ts, spec) are synonymous, not shared; reuse needs a runtime edit, so it is reported, not done. H2: the runtime compares name === 'default', the spec side copies that comparison, and every other spelling is already refused by DatasourceSchema.name's pattern (pinned). H3: re-measured; the R4 shape exits 1 with AppPlugin's refusal under OS_DATABASE_URL and under --dev, so no config that boots today is newly refused. H4: the gate output decided the disposition (adr-0087 registration green with registered). H5: the skills line is in open_questions.",
"tests": "Head a6f3e6e (branch merged with origin/main a869ea6 via scripts/pm/os-regen-merge.sh). spec: build exit 0; check:generated: all 14 up to date; src/stack-datasource-host-reserved-name.test.ts + scripts/dropped-refinements.test.ts + src/migrations/migrations.test.ts 212/212; full pnpm --filter @objectstack/spec test (pre-merge, at 65b8573+) 679 files passed and 1 red (the ledger header total droppedRefinementSites), fixed in 109c9e9 and then 27/27; typecheck exit 0. cli: the door pin test/stack-datasource-default-validate-door.test.ts (integration) 2/2 on the merge head; vitest run --project unit 293/295 files at first, and the 2 reds were 'packages/cli is not built' (published-subpath-console/hook-body pins); after pnpm --filter @objectstack/cli build they were 29/29; typecheck exit 0. runtime: src/default-datasource-plugin.test.ts 12/12 (AppPlugin's own refusal pin, unchanged). Ablation, valid leg (ablate3): the refusal body was replaced by if ((globalThis as any).__ablation23634 === undefined) return; via scripts/ablation-replace.mjs (marker count 0 to 1); spec build exit 0; ablation-dist-preflight planted: marker in 12 dist files, exit 0; spec pins 4 failed / 3 passed (4 refusals red, 3 controls green); CLI door 1 failed / 1 passed. Restore: git diff HEAD empty, hash equal to the HEAD blob, rebuild exit 0, preflight --absent clean in 232 files, spec 7/7, CLI 2/2. Two earlier legs were no-ops and are not counted. Leg 1 deleted the .superRefine, and the mutated build failed on the dropped-refinements ledger, so dist stayed unmutated (preflight --absent exit 1); the spec leg read src and went red, the CLI leg stayed green and is void. Leg 2 used a string-literal guard, which tsup constant-folded out of dist (preflight planted exit 1). Boot re-measure (H3, source CLI bin/run-dev.js serve, host config with datasources [{name:'default', driver:'sqlite'}] through defineStack strict:false): OS_DATABASE_URL=file:... exit 1 and --dev exit 1, both with [AppPlugin] app 'com.example.fx' declares a datasource named 'default' — that name is reserved for the host's primary datasource. ...",
"gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path): 90 families for 6 paths. Each was run one by one with the exit code captured before any pipe. On a085cc9: 90 exit 0. First-run reds: check-plugin-teardown-shape --self-test exit 3 (shallow clone, pinned fixture commit 621a487 unreachable; it passed after a depth-1 fetch of that commit), check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: unbuilt sibling dists; exit 0 on rerun once built), and check:stack-collection-maps exit 1 (a real finding: a multi-line z\\n.array( hid datasources from its census; fixed in a085cc9). Rerun on the merge head a6f3e6e: 90/90 exit 0. --ran with CMD :: exit N lines: 'Run reconciliation — 90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN', a DERIVED zero. Also run: check:adr-0087-registration ✓ (registered stack-datasource-host-reserved-name-refused, new here), check:dev-prereqs exit 0 (local-only). CI check-runs on PR 23757: in_progress at report time, not awaited.",
"line_budget": "+352 / -2 over 6 files (dispatch-gates: 354 changed lines vs the 3000 human-merge threshold: under). stack.zod.ts +38/-1; spec pin 113; cli pin 133; D3 entry 30; changeset 32; ledger +5/-1.",
"files_changed": [
".changeset/23634-stack-datasource-default-refused.md",
"packages/cli/test/stack-datasource-default-validate-door.test.ts",
"packages/spec/dropped-refinements.baseline.json",
"packages/spec/src/migrations/entries/semantic/18.stack-datasource-host-reserved-name-refused.ts",
"packages/spec/src/stack-datasource-host-reserved-name.test.ts",
"packages/spec/src/stack.zod.ts"
],
"deviations": [
"packages/spec/dropped-refinements.baseline.json is outside the claim's named surface. The spec build refuses until the new refinement's 4 dropped sites (installed-package manifest.datasources) are recorded, and its header total moves 759 to 763. This is a gate-required artifact under the claim's 'generated outputs if the gates ask' clause, and it is hand-edited by that ledger's design. No arm of refinement-projection.ts fits, and adding one is a public-contract decision, so none was added.",
"The first commit cca45a7 (wip) carries no Claude-Session / Co-authored-by trailer pair; every later commit does. Pushed history was not rewritten.",
"The CLI unit tier needed a cli build that the dependency-closure build does not produce; 2 files were re-run after the build (see tests).",
"Merged origin/main (8 commits, including PR 23418's registry.ts.template edit) via os-regen-merge.sh after the gates had run, then re-ran build, check:generated, the pins and all 90 derived gates on the merge head. Package dists other than spec/cli were not rebuilt after the merge; CI builds fresh."
],
"mcp_calls": "4 — mcp__github__issue_read (get, get_comments on 23634), mcp__github__pull_request_read (get_files on 23331 for precedent; get on 23757 for the draft/assignee read-back). No write tools.",
"api_writes": "2 relay strokes as objectstack-fleet[bot]: (1) scripts/pm/fleet-write/dispatch.mjs pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23757/assignees [os-bill], run 38828501816 success, body read back byte-identical (8158 bytes); (2) scripts/pm/post-stamped.mjs → POST /repos//issues/23634/comments (this os-dev-report). Plus git pushes of the branch (not REST). No label write (none named by the dispatch).",
"open_questions": [
{
"question": "skills/objectstack-data/rules/datasources.md:4-6 reads: 'Objects route to one via their datasource field (default: 'default'). Most apps need only the default datasource; declare more to read/write a separate or external database.' 'Declare more' implies default is declared too, so it invites the refused shape. Should the skills lane reword it?",
"options": [
"A: reword to 'Most apps need only the host's default datasource, which the host connects from OS_DATABASE_URL; never declare a datasource named default (the name is host-reserved and refused). Declare a datasource under its own name only to read/write a separate or external database.'",
"B: leave it; the authoring refusal now catches the shape at os validate"
],
"recommendation": "A. AI-proofing: the published skill is what an AI reads first, and the refusal should not be the first place it learns the rule. Long-term fit: the skill then states the contract the spec enforces. Startup focus: one line, net zero or less. Business need: the only measured producer of the shape was an agent."
},
{
"question": "The reserved-name refusal is now said in three places (runtime app-plugin.ts, the admin door datasource-admin-service.ts, spec stack.zod.ts): synonymous, not shared. Should spec export the reserved name and refusal text for the runtime to reuse?",
"options": [
"A: export the name constant and the sentence builder from @objectstack/spec, and have AppPlugin and the admin service import them (runtime + service-datasource edits, an api-surface addition)",
"B: keep three synonymous sentences, each owned by its door"
],
"recommendation": "A, as a follow-up in the runtime lane. Long-term fit: one source for one contract. AI-proofing: no drift between the doors. Startup focus: small, but it adds a public export, so it needs its own card and not this PR's surface."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed: the published JSON Schema stays wider than the parse for this rule (it is recorded in the dropped-refinements ledger, like the 759 sites before it); closing it needs a new refinement-projection arm, which that module calls a public-contract decision"
]
}
0a052e6705 "feat(spec)!: a stack's datasources collection refuses a datasource named default, the host-reserved name (#23757)", merged through the queue at 2026-10-16T01:27Z (git branch -r --contains → origin/main)
The squash tree aad16cefdc4c equals git merge-tree --write-tree of its parent eae3b389d2 with a6f3e6e812. That head carries contract review PASS 6205386930 and ACCEPT 6205431264.
card
closed completed by Fixes #23634
What ships. A stack's datasources collection refuses an entry named default at datasources.N.name, with the runtime's sentence and remedy (set OS_DATABASE_URL, or declare the datasource under another name and route objects to it). It reaches defineStack, os validate and publish. DatasourceSchema and the host's own default row are unchanged. The D3 entry is stack-datasource-host-reserved-name-refused.
Activity
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
bug·priority:p2·domain:cli·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T09:00ZRead fresh. A host config that declares
datasources: [{ name: 'default', …, autoConnect: true }]cannot boot a productionserve. This was measured at the public door (boot R4).No driver available for object 'sys_organization'.AppPlugin.start()connects config datasources afterAuthPlugin.start()already needs a driver (start order measured in6184721596).Grade p2: a declared config shape that never serves the boot it declares. The operator gets a refusal or a crash, not silence. Other storage routes work: a driver plugin,
OS_DATABASE_URL,--dev.Lane
domain:cli(packages/runtime,AppPlugin, by the lane table).Order: none. PR #23631 merged at 08:54Z (
edac2183c0). When the declared default serves the boot, this card's PR restores "declare a default datasource" to that PR's no-storage remedy line inserve.ts.Scope:
startneeds a driver.findingleaves on grading.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 16 (#23634: a host config's declared default datasource (
autoConnect) is connected before any plugin'sstart()needs a driver, so a productionserveover it boots;serve's no-storage remedy names "declare a default datasource" again) · 2026-10-15T09:07ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23634-declared-datasource-boot-orderWorktree:
objectstack-issue-23634Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)Scope:
batch3, by 「并发恢复3」. This is the third slot, beside #23558 and #23546.Order, read at this act: after the lane's p1 #23546 (claimed in this round) and the
target:v18#23558 (in flight), this is the lane's oldest dispatchable p2bug. #23531's remainder waits on #23586, #23497 ispm:blockedon #23632, and #23050 is seat 1's. Triage6188422721named no order condition. PR #23631, whose remedy line this card restores, merged at 08:54Z.File surface (read on
origin/main; stop on a breach and explain it in the report):packages/runtime/src/app-plugin.ts: where config datasources are connected (today instart(), afterAuthPlugin.start()needs a driver). Only the earliest point at which a declared default datasource (autoConnect) is connected and registered as a driver.packages/cli/src/commands/serve.ts: only the no-storage guard'snoStorageRemedieslist (:5143-:5147) and the refusal's one-lineErrortext (:5170), to name "declare a default datasource" again once it works. ⛔ No other step..changeset/23634-*.md.packages/objectql(objectql: the "sys_metadata could NOT be read at boot" ERROR tells the operator to "check the datasource behind sys_metadata" when no datasource is declared and no driver is registered #23636), notplugin-auth, notpackages/core's plugin ordering, not the drivers.Container & model:
M, judged (a runtime start-order change for declared datasources, production-boot pins),mode:subagent,model: default tier. Ceiling:Clause-②: no. A config that boots today keeps booting, and a declared default that never served now serves.Clause-②: no
Responsibility:
the declared-datasource connect timing in packages/runtime AppPlugin (this lane) and serve's remedy line | plugin-auth's driver need at start(), read | every operator whose host config declares its default datasource rather than OS_DATABASE_URL or a driver pluginThread-read: 6188422721
Serial constraints cleared:
app-plugin.tsorserve.ts(read at this act).runtime/src/domains/*, a different region.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 23634,
"status": "needs_decision",
"branch": "claude/issue-23634-declared-datasource-boot-order",
"pr": null,
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the parent's session; this run is mode:subagent)",
"premise_still_valid": false,
"summary": "The card's symptom is real, but its mechanism is not why the declared default never serves. R4 declares
datasources: [{ name: 'default', ... autoConnect: true }], and AppPlugin.start() refuses a stack-declared datasource nameddefaultin every posture: with OS_DATABASE_URL (M2), with --dev (M3) and on the standalone path (M4). The refusal reads "declares a datasource named 'default' - that name is reserved for the host's primary datasource" (app-plugin.ts:1120-1126). It is documented in drivers.mdx:265 ("The namedefaultis host-reserved: an app bundle declaring a datasource with that name is rejected at load") and enforced again at the admin door (datasource-admin-service.ts:1091). So connecting it earlier only swaps PR #23631's refusal (M1) for the reserved-name refusal (M2). For any other name, hypothesis (b)'s init-time connect would change which datasource a config that boots today resolves to. The connect would land before ObjectQLPlugin.start() adopts a stack plugin'sdriver.*, and the engine makes its first-registered driver the default (engine.ts:8327,isDefault || this.drivers.size === 1). M9 boots today with defaultcom.objectstack.driver.sql; that config would move every unbound object, platform objects included, ontoarchive. Both fixes are a contract change or a resolution change, so I stopped per the ruling: no code, no PR, and serve.ts's remedy line is left as PR #23631 wrote it, because the condition for restoring it ("once the declared default serves the boot") is not met. The options are in open_questions.","tests": "No test was written (no code). Measurement only. Tree: objectstack-ai/objectstack at 7dc7d22 (BASE, origin/main; PR #23631 = edac218 is in it). Build:
os-verify-lock.sh -c \"NODE_OPTIONS=--max-old-space-size=4096 pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2\": Tasks 61 successful, 61 total; VERDICT command-exit 0. The first attempt was VERDICT queue-timeout (exit 99) and is NOT MEASURED. Each boot:node packages/cli/bin/run.js serve objectstack.config.ts -p PORTon the built CLI. NODE_ENV was unset (production) and OS_CLOUD_URL=off. OS_DATABASE_URL was unset unless noted. OS_AUTH_SECRET was a fixture literal. Fixtures are #23573's probe2.mjs host fixtures plus 5 variants (probe3.mjs, in the parent's scratchpad under issue-23634/p). Results: M1 R4 (name: 'default', sqlite, autoConnect), production: exit 1, PR #23631's refusal 'Refusing to boot: this production serve has no storage ...'. M2 R4 + OS_DATABASE_URL=file:ctl.db: exit 1; '[fx] host-marker start' printed, then 'Plugin plugin.app.com.example.fx failed to start - rollback complete: [AppPlugin] app com.example.fx declares a datasource named default - that name is reserved for the host's primary datasource. Rename it (e.g. fx_primary) and route objects to it explicitly, or omit it to use the host default.' M3 R4 + --dev: exit 1, the same refusal. M4 the samedefaulton a non-host config (standalone path), production: exit 1, the same refusal. V1os validate objectstack.config.tson R4, withsharingModel: 'private'added to clear the unrelated security-owd-unset rule: exit 0, 'Data: 1 Objects 1 Fields 1 Datasources'. M5 a host config withdatasourceMapping: [{ default: true, datasource: 'primary' }]over a declared sqliteprimary(the shape resolve-project-database.ts treats as the config-declared default), production: exit 1, PR #23631's refusal. M6 M5 withautoConnect: trueon primary: exit 1, the same. M7 M5 + OS_DATABASE_URL=file:ctl.db: 'Server is ready', health 200, data 401, then 59 'DATABASE_ERROR ... no such table' lines on platform objects (sys_metadata 10, sys_setting 7, sys_platform_setting 7, sys_user_permission_set 6, sys_migration 6, sys_organization 2, and more). Opened read-only afterwards: ctl.db held 72 tables and 1 sys_organization row; ds-primary.db held 13 tables and 'no such table: sys_organization'. M8 M5 + --dev: ready, health 200, 61 DATABASE_ERROR lines. M9 R9 (plugin com.acme.store registers a SqlDriver asdriver.*in init()) plus a declaredarchivewith autoConnect, production, --log-level debug: ready, health 200. The log read 'Registered driver com.objectstack.driver.sql', then 'Set default driver com.objectstack.driver.sql' (ObjectQLPlugin.start adoption, plugin.ts:965), then 'Registered driver archive' and 'Auto-connected declared datasources [archive]' (AppPlugin.start). No ablation: nothing was changed.","gates": "None owed: the branch has no diff.
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackexited 2 with 'this branch changes nothing against origin/main (merge base 7dc7d22) - nothing to derive' (the repo assertion held). No typecheck, no pnpm lint and no ablation, because no file was edited. No PR, so there are no check-runs.","mcp_calls": "0",
"api_writes": "1 relay stroke: scripts/pm/post-stamped.mjs, which sends POST /repos//issues/23634/comments (this os-dev-report) as objectstack-fleet[bot] through the fleet-write relay. Also one git push of the empty branch claude/issue-23634-declared-datasource-boot-order at BASE 7dc7d22 (the write-route probe; not REST). Reads (session identity, GET only): issues/23634, its /comments, issues/comments/6184721596 and pulls/23631. No PR, no label write, no assignee write.",
"open_questions": [
{
"question": "What is 'a host config's declared default datasource', and should it serve a bare production
serve? The R4 spelling (datasources[].name === 'default') is refused by the host-reserved contract in every posture (M2-M4), andos validatepasses it (V1). The spelling the shared resolver recognises (datasourceMapping { default: true, datasource: NAME }, resolve-project-database.ts readConfigDeclaredDefault, maintainer ruling recorded in its header) is honoured byos start/os migrate/ standalone serve, but not by host-config serve's storage step. Moving AppPlugin's connect into init() (hypothesis b) fixes neither: R4 is still refused at start, and for any other name it changes M9's default driver.","options": [
"A: Keep the reservation, and refuse
defaultat authoring instead of after auth starts. The stack'sdatasourcescollection refuses a datasource nameddefault. The refusal goes on that collection, not on DatasourceSchema, whose documents include the host's owndefaultrow. defineStack,os validateand publish then reject R4 with the remedy (set OS_DATABASE_URL, or declare a datasource under its own name and route objects to it). The published skill line skills/objectstack-data/rules/datasources.md:5 ('Most apps need only thedefaultdatasource; declare more ...') is reworded so it no longer reads as an invitation to declare it. serve's remedy line stays as PR #23631 wrote it, and this card's runtime half closes as 'not declarable'. Business need: measured in-repo producers of R4 are zero (examples, create-objectstack, skills, docs). Its only measured producer is the agent-written fixture R4 itself. Every in-repo production entry goes throughos start, which hands serve an OS_DATABASE_URL (read in 6184721596). Long-term fit: one contract is kept:defaultis host-owned (drivers.mdx:265, admin door :1091), and the shared resolver already declines adefault-named rule (resolve-project-database.ts:224). The refusal moves from Phase 2 to authoring, which is contract-first. AI-proofing: strongest. The natural AI spelling is refused atos validatewith its remedy, where today it passes there (V1) and dies after auth starts (M2). Startup focus: S to M, and no capability expansion. It is a contract tightening, not a new CI gate. Surface: packages/spec (the stack schema) plus the skill line, both outside this claim. Changes no config that boots today: R4 boots in no posture.","B: The host config's own
defaultbecomes the host's primary when the host declared none. When a production host-config serve's storage step resolves nothing, serve hands the config'sdefaultentry to DefaultDatasourcePlugin, the existing Phase-1 connect (asDefault, bootCritical). AppPlugin then skips that entry instead of refusing it. With OS_DATABASE_URL or --dev, the reservation keeps refusing it, as today (M2, M3). Business need: the same zero producers, and no named operator. Long-term fit: it adds a second spelling of 'the project's default database' beside the mapping rule, andos start/os migratekeep ignoring it, which reopens the per-door fork the shared resolver closed. AppPlugin cannot decide 'the host declared no default' in its own init(). DefaultDatasourcePlugin precedes it only by insertion order, and a stack plugin's driver arrives later, so per AGENTS.md 'Startup registry reads' serve has to pass AppPlugin an explicit signal. AI-proofing: weak. The same declaration boots in one posture and is refused in two. Startup focus: M. It touches serve.ts's storage step (outside this claim's serve.ts surface), the app-plugin.ts reservation and the drivers.mdx contract text. Changes no config that boots today.","C: Host-config serve honours the mapping-rule declared default, through the shared resolver. In production, when the storage step resolves nothing, serve consults the same
datasourceMapping { default: true }plusdatasources[]resolution thatos startuses (today artifact-only; it needs a config-object entry point in packages/runtime) and hands the result to DefaultDatasourcePlugin. The remedy line then names that rule. R4 stays refused, and pairs with A's authoring half. Business need: zero in-repo producers of the mapping shape. examples/app-crm deleted its rule, and its config comment records why. Long-term fit: best convergence, since every door resolves one project's default the same way (option E in 6184721596). AI-proofing: medium. The mapping spelling is less natural, so R4's spelling still needs A. Startup focus: M, and a capability expansion with no measured pull. It lands outside this claim (runtime resolve-project-database.ts plus serve.ts's storage step). A production no-URL host config that boots today on a stack or onEnable driver and also carries a mapping default would gain a second default, so its precedence needs ruling. It inherits M7's split brain unless the rule is installed before boot writes, and that is objectql's, a hold-off.","D (the dispatched hypothesis b, for the record): AppPlugin.init() connects autoConnect declared datasources before any start(). This is rejected on measurement. R4 is still refused at start (M2-M4). For any other name, the init-time connect precedes ObjectQLPlugin.start's
driver.*adoption, and engine.ts:8327 makes it the default. M9 boots today on com.objectstack.driver.sql; under D it would move toarchive, platform objects included. That changes an existing config's resolution, which the ruling forbids."],
"recommendation": "A, on all four axes. Business need: neither declared-default spelling has an in-repo producer, the only R4 producer measured is an agent-written fixture, and every in-repo production entry already supplies OS_DATABASE_URL through
os start. Long-term fit: it keeps one contract (defaultis host-owned) and the one shared resolution, where B forks both. AI-proofing: it turns a late Phase-2 refusal into an authoring refusal with the remedy, at the exact door that passes the shape today (V1). Startup focus: it expands no capability. It also leaves PR #23631's remedy line correct as written, since that line deliberately does not send an R4 operator to the thing they already did. C is the coherent route only if a named operator needs a bare productionserveover a config-declared default; it then needs triage scope beyond this claim, and objectql's timing fix first (M7). Either way this card's claimed surface (app-plugin.ts connect point plus serve.ts remedy text) cannot deliver the scope as written."}
],
"out_of_scope_findings": [
"class: a · reach: public door
objectstack serve objectstack.config.ts(production, OS_DATABASE_URL=file:ctl.db; also --dev) on a host config declaringdatasourceMapping: [{ default: true, datasource: 'primary' }]over a declared sqliteprimary(M7, M8): 'Server is ready' and health 200, then 59 (URL) / 61 (--dev)DATABASE_ERROR ... no such tablelines on platform objects (sys_metadata, sys_setting, sys_organization, ...). The URL store holds 72 tables and the Default Organization; the mapped primary holds 13 tables and no sys_organization. Read in code, not measured: AppPlugin.start() installs the mapping (ql.setDatasourceMapping), and that runs after ObjectQLPlugin.start's schema sync and AuthPlugin.start's Default Organization, both of which used the default store. After it, every unbound object routes to a datasource whose schema never synced. The routing order is objectql's (resolveDatasourceBinding, step 2 before step 5). Landing: objectql or runtime boot order; objectql is #23636's hold-off. Producer: none in-repo (examples/app-crm deleted its rule). · dedupe words: datasourceMapping default rule platform objects no such table; mapping installed after schema sync split brain; sys_organization routed to mapped datasource after boot","class: c · reach:
os validate objectstack.config.tsexit 0 ('1 Datasources', V1) on a config declaringdatasources: [{ name: 'default', ... }], which every serve posture refuses at AppPlugin.start (M2-M4). Producer: the R4 fixture, written by an agent for #23573's measurement and then read by this card as 'a declared default datasource'. Likely seed: skills/objectstack-data/rules/datasources.md:5 ('Most apps need only thedefaultdatasource; declare more ...'). This is option A's subject: if A is chosen, the seat folds it into that card rather than filing it separately. · dedupe words: datasource named default reserved validate passes; host-reserved default datasource authoring refusal; os validate default datasource AppPlugin refuses at start","carrier: none · noted, not filed (read, not measured beyond M9): engine.ts:8327 makes the first driver to reach
registerDriverthe engine default, so which driver serves unbound objects (platform objects included) depends on Phase order rather than on any declaration whenever no driver is registered asDefault. Measured consequence today: none wrong (M9 resolves to the stack plugin's driver). It is the reason hypothesis (b) changes resolution."],
"deviations": [
"Stopped at the ruling's guard ('If a fix would change which datasource an existing config resolves to, STOP and report needs_decision'; 'If the only fix is a plugin-order or contract change outside this surface, STOP and report'). No code, no PR, no changeset, and serve.ts's remedy line was not touched: the ruling restores it only 'once the declared default serves the boot', and no in-surface fix makes it serve.",
"premise_still_valid is false for the card's stated cause, not for its symptom. R4 never serves (true). The cause is the host-reserved-name refusal at AppPlugin.start, not only the connect timing: with a driver present (M2, M3) the same declaration is refused.",
"The branch claude/issue-23634-declared-datasource-boot-order is pushed at BASE 7dc7d22 with no commits (the write-route probe). The worktree ../objectstack-issue-23634 is removed after this report (git worktree remove, no --force); it holds no edits.",
"R9-R13 from #23573 were not re-measured: nothing changed, so nothing can regress. M9 is a new boots-today shape, measured because it decides hypothesis (b).",
"All fixtures are throwaway projects in the parent's scratchpad (issue-23634/p): @objectstack/spec, driver-sql, plugin-auth, objectql and organizations are symlinked in, and auth secrets are fixture literals. The probe SIGTERMs each process it starts. The V1 copy (vfx) differs from the R4 fixture only by
sharingModel: 'private'(grep count 1).","The dispatch's gate list (dispatch-gates list, typecheck, pnpm lint, ablation) was not run: there is no diff to gate (dispatch-gates exit 2)."
],
"files_changed": []
}
objectstack-fleet commentedon Oct 15, 2026
Release:
domain:cliseat 2 ·sunhaolin·session_01Q9PZ5Pzqcus4woVvTZwWNU· 2026-10-15T09:41Z · cause: the card's premise is false (this seat's filing error), and every fix is a contract or resolution change outside the claim · destination:pm:queue+pm:retriage, unassignedThe readings dev ran under claim
6188533413and stopped at the ruling's guard (os-dev-reporton this card). No code, no PR.This seat's correction, in public. This card was filed from #23573's out-of-scope finding, and it named the wrong cause.
AuthPlugin.start().datasources: [{ name: 'default', … }].AppPlugin.start()refuses a stack-declared datasource nameddefaultin EVERY posture, because the name is reserved for the host's primary: "declares a datasource named 'default' — that name is reserved for the host's primary datasource" (app-plugin.ts:1120-:1126). It is documented atdrivers.mdx:265and enforced again at the admin door (datasource-admin-service.ts:1091).OS_DATABASE_URL), M3 (--dev) and M4 (the standalone path) all exit 1 with that refusal. So connecting it earlier only swaps PR fix(cli): a production serve with no storage says so before any plugin starts, and refuses when its auth service needs a driver #23631's refusal for the reserved-name refusal.engine.ts:8327). M9 boots today oncom.objectstack.driver.sqland would move every unbound object, platform objects included, ontoarchive.create-objectstack, the skills or the docs.The options the readings name (four-axis analysis in the report's
open_questions):defaultat authoring.datasourcescollection refuses that name, sodefineStack,os validateand publish reject R4 with its remedy. Todayos validatepasses it (V1).skills/objectstack-data/rules/datasources.md:5("Most apps need only thedefaultdatasource; declare more …").packages/specand the skill line, outsidedomain:cli, and it is a contract tightening of an authoring shape that boots in no posture.defaultbecomes the primary when the host declared none. This adds a second spelling of the project default, and doors would fork again.servehonours the shared resolver's mapping-rule default (datasourceMapping { default: true }). It is the best convergence, but there is no measured pull, and it inherits the split-brain finding below.AppPlugin.init().PR #23631's remedy line stays as written. Its condition to change ("once the declared default serves the boot") is not met, and it deliberately does not send an R4 operator to the thing they already did.
Filed from the same readings: the
datasourceMappingdefault split brain (M7, M8) as its own card. It is linked below once filed.Asked of triage: re-route this card per option A (lane
domain:specfor the stack schema, plus the skill line), or close it in favour of a new A card, or rule otherwise.Generated by Claude Code
4 remaining items
[-]runtime: a host config's declared default datasource (autoConnect) never serves a production serve's boot — AppPlugin.start connects config datasources after AuthPlugin.start already needed a driver for sys_organization[/-][+]spec: the stack's `datasources` accepts a datasource named `default`, which every boot refuses as host-reserved — `defineStack` / `os validate` / publish pass a config that boots in no posture[/+]objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 7 (second shift, parallel again) · 2026-10-15T22:11Z · triage's proxy ruling A (
6189356908): the stack'sdatasourcescollection refuses a datasource nameddefault, which every boot refuses as host-reserved. SodefineStack,os validateand publish say the runtime's sentence and remedy at authoring time. One PR,Fixes #23634.Session:
session_01LFqdRsuM5nkqoJdHd5ztVk(the seat; the dev is a cloud session whose draft PR carries its own session link)Account:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23634-stack-datasource-default-refusedWorktree:
objectstack-issue-23634Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/main9bb93706for later; stop on a breach and explain it in the report):packages/spec/src/stack.zod.ts: thedatasourcescollection only, with the refusal and its remedy (setOS_DATABASE_URL, or declare the datasource under another name and route objects to it).packages/spectest fordefineStack, plus theos validatepin where the CLI's validate pins live. The ruling's two CONTROLS go beside them: a datasource namedfx_primarypasses, and the host's owndefaultrow still parses throughDatasourceSchema..changeset/23634-*.md.packages/spec/src/data/datasource.zod.ts(DatasourceSchemadocuments the host's owndefaultrow). ⛔ Notpackages/cli/src/commands/serve.ts: PR fix(cli): a production serve with no storage says so before any plugin starts, and refuses when its auth service needs a driver #23631's remedy line stays as written. ⛔ Notskills/**: ifskills/objectstack-data/rules/datasources.mdinvites declaringdefault, the report gives the line and the rewording, for the skills lane.Container & model:
M,mode:cloud,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Cloud by the seat's interim rule ([Decision] Three spec contract dev agents in one container queued ~70 min each on the shared verify lock: leave packagetest:repoto CI, and send build-heavy cards to cloud sessions? #23607):packages/specplus the CLI's validate pins is a build-heavy closure. Review ceilingCONTRACT_REVIEW_TIER, reason:packages/spec's published accept set narrows.Clause-②: no
Responsibility:
packages/spec's stack schema accepts a datasource nameddefaultthatAppPlugin.start()refuses at every boot as host-reserved | none:os validatepasses it (V1, report6189075724); the refusal arrives only at boot | an author, human or AI, who declares adefaultdatasource in a stack; no in-repo producer was found, and the one instance is an agent-written fixtureThread-read: 6189356908
Ruling-ref: 6189356908 (triage's proxy ruling A,
auto-adjudicated; not a decision card)Serial constraints cleared:
batch 3in this seat's session. In flight from this seat: spec:PageTypeSchemasayshomeis "the platform-level landing page" andutilitya "floating utility panel", but the landing is the first navigation item and both types render as ordinary inline pages #23610's objectui half (cloud) and spec(conversions): eight ADR-0087 D2 conversions stay on the 18 load path after #23487 — five families each need a ruling before they can retire #23543 F3, dispatched in the same act on a disjoint surface (flow nodes and conversions).stack.zod.ts,datasource.zod.tsor the CLI's validate. PR feat(spec)!: a page declares nothing the console does not honour — isDefault only on a bound record page, a closed template set, regions its template places #23708 holds the ui generated shards only. Draft PR feat(lint)!: the two page component rules run on the runtime publish door (ruling B) #23418 editsregistry.ts.template: if this PR adds a migration entry, the later lander mergesmainand regenerates throughscripts/pm/os-regen-merge.sh.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23634,
"status": "done",
"branch": "claude/issue-23634-stack-datasource-default-refused",
"pr": "#23757",
"session": "session_01UvyD7nLUx7XGKKQVF37FY9",
"premise_still_valid": true,
"summary": "Ruling A is implemented. The stack's
datasourcescollection (STACK_DEFINITION_COLLECTIONS_SHAPE in packages/spec/src/stack.zod.ts) carries a superRefine that refuses an entry named exactlydefault, at datasources.N.name with code custom. The text carries the runtime's sentence ("declares a datasource named 'default' — that name is reserved for the host's primary datasource") and the ruling's remedy (set OS_DATABASE_URL and omit the entry, or declare it under another name and route objects to it). DatasourceSchema, serve.ts and skills/** are untouched. defineStack (array and map form), the bare ObjectStackDefinitionSchema parse (the os validate door) and os validate --json are pinned, beside both CONTROLs (fx_primary passes; the host's owndefaultrow parses through DatasourceSchema). There is a D3 semantic entrystack-datasource-host-reserved-name-refusedand a BREAKING-minor changeset with theregistereddisposition. The dropped-refinements ledger gains the 4 installed-package manifest.datasources sites (759 to 763), because the build requires it. The PM hypotheses were measured as follows. H1: the three sentences (app-plugin.ts, datasource-admin-service.ts, spec) are synonymous, not shared; reuse needs a runtime edit, so it is reported, not done. H2: the runtime comparesname === 'default', the spec side copies that comparison, and every other spelling is already refused by DatasourceSchema.name's pattern (pinned). H3: re-measured; the R4 shape exits 1 with AppPlugin's refusal under OS_DATABASE_URL and under --dev, so no config that boots today is newly refused. H4: the gate output decided the disposition (adr-0087 registration green withregistered). H5: the skills line is in open_questions.","tests": "Head a6f3e6e (branch merged with origin/main a869ea6 via scripts/pm/os-regen-merge.sh). spec: build exit 0; check:generated: all 14 up to date; src/stack-datasource-host-reserved-name.test.ts + scripts/dropped-refinements.test.ts + src/migrations/migrations.test.ts 212/212; full
pnpm --filter @objectstack/spec test(pre-merge, at 65b8573+) 679 files passed and 1 red (the ledger header total droppedRefinementSites), fixed in 109c9e9 and then 27/27; typecheck exit 0. cli: the door pin test/stack-datasource-default-validate-door.test.ts (integration) 2/2 on the merge head;vitest run --project unit293/295 files at first, and the 2 reds were 'packages/cli is not built' (published-subpath-console/hook-body pins); afterpnpm --filter @objectstack/cli buildthey were 29/29; typecheck exit 0. runtime: src/default-datasource-plugin.test.ts 12/12 (AppPlugin's own refusal pin, unchanged). Ablation, valid leg (ablate3): the refusal body was replaced byif ((globalThis as any).__ablation23634 === undefined) return;via scripts/ablation-replace.mjs (marker count 0 to 1); spec build exit 0; ablation-dist-preflight planted: marker in 12 dist files, exit 0; spec pins 4 failed / 3 passed (4 refusals red, 3 controls green); CLI door 1 failed / 1 passed. Restore: git diff HEAD empty, hash equal to the HEAD blob, rebuild exit 0, preflight --absent clean in 232 files, spec 7/7, CLI 2/2. Two earlier legs were no-ops and are not counted. Leg 1 deleted the .superRefine, and the mutated build failed on the dropped-refinements ledger, so dist stayed unmutated (preflight --absent exit 1); the spec leg read src and went red, the CLI leg stayed green and is void. Leg 2 used a string-literal guard, which tsup constant-folded out of dist (preflight planted exit 1). Boot re-measure (H3, source CLI bin/run-dev.js serve, host config with datasources [{name:'default', driver:'sqlite'}] through defineStack strict:false): OS_DATABASE_URL=file:... exit 1 and --dev exit 1, both with[AppPlugin] app 'com.example.fx' declares a datasource named 'default' — that name is reserved for the host's primary datasource. ...","gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path): 90 families for 6 paths. Each was run one by one with the exit code captured before any pipe. On a085cc9: 90 exit 0. First-run reds: check-plugin-teardown-shape --self-test exit 3 (shallow clone, pinned fixture commit 621a487 unreachable; it passed after a depth-1 fetch of that commit), check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET: unbuilt sibling dists; exit 0 on rerun once built), and check:stack-collection-maps exit 1 (a real finding: a multi-line
z\\n.array(hid datasources from its census; fixed in a085cc9). Rerun on the merge head a6f3e6e: 90/90 exit 0. --ran withCMD :: exit Nlines: 'Run reconciliation — 90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN', a DERIVED zero. Also run: check:adr-0087-registration ✓ (registered stack-datasource-host-reserved-name-refused, new here), check:dev-prereqs exit 0 (local-only). CI check-runs on PR 23757: in_progress at report time, not awaited.","line_budget": "+352 / -2 over 6 files (dispatch-gates: 354 changed lines vs the 3000 human-merge threshold: under). stack.zod.ts +38/-1; spec pin 113; cli pin 133; D3 entry 30; changeset 32; ledger +5/-1.",
"files_changed": [
".changeset/23634-stack-datasource-default-refused.md",
"packages/cli/test/stack-datasource-default-validate-door.test.ts",
"packages/spec/dropped-refinements.baseline.json",
"packages/spec/src/migrations/entries/semantic/18.stack-datasource-host-reserved-name-refused.ts",
"packages/spec/src/stack-datasource-host-reserved-name.test.ts",
"packages/spec/src/stack.zod.ts"
],
"deviations": [
"packages/spec/dropped-refinements.baseline.json is outside the claim's named surface. The spec build refuses until the new refinement's 4 dropped sites (installed-package manifest.datasources) are recorded, and its header total moves 759 to 763. This is a gate-required artifact under the claim's 'generated outputs if the gates ask' clause, and it is hand-edited by that ledger's design. No arm of refinement-projection.ts fits, and adding one is a public-contract decision, so none was added.",
"The first commit cca45a7 (wip) carries no Claude-Session / Co-authored-by trailer pair; every later commit does. Pushed history was not rewritten.",
"The CLI unit tier needed a cli build that the dependency-closure build does not produce; 2 files were re-run after the build (see tests).",
"Merged origin/main (8 commits, including PR 23418's registry.ts.template edit) via os-regen-merge.sh after the gates had run, then re-ran build, check:generated, the pins and all 90 derived gates on the merge head. Package dists other than spec/cli were not rebuilt after the merge; CI builds fresh."
],
"mcp_calls": "4 — mcp__github__issue_read (get, get_comments on 23634), mcp__github__pull_request_read (get_files on 23331 for precedent; get on 23757 for the draft/assignee read-back). No write tools.",
"api_writes": "2 relay strokes as objectstack-fleet[bot]: (1) scripts/pm/fleet-write/dispatch.mjs pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23757/assignees [os-bill], run 38828501816 success, body read back byte-identical (8158 bytes); (2) scripts/pm/post-stamped.mjs → POST /repos//issues/23634/comments (this os-dev-report). Plus git pushes of the branch (not REST). No label write (none named by the dispatch).",
"open_questions": [
{
"question": "skills/objectstack-data/rules/datasources.md:4-6 reads: 'Objects route to one via their
datasourcefield (default:'default'). Most apps need only thedefaultdatasource; declare more to read/write a separate or external database.' 'Declare more' impliesdefaultis declared too, so it invites the refused shape. Should the skills lane reword it?","options": [
"A: reword to 'Most apps need only the host's
defaultdatasource, which the host connects fromOS_DATABASE_URL; never declare a datasource nameddefault(the name is host-reserved and refused). Declare a datasource under its own name only to read/write a separate or external database.'","B: leave it; the authoring refusal now catches the shape at os validate"
],
"recommendation": "A. AI-proofing: the published skill is what an AI reads first, and the refusal should not be the first place it learns the rule. Long-term fit: the skill then states the contract the spec enforces. Startup focus: one line, net zero or less. Business need: the only measured producer of the shape was an agent."
},
{
"question": "The reserved-name refusal is now said in three places (runtime app-plugin.ts, the admin door datasource-admin-service.ts, spec stack.zod.ts): synonymous, not shared. Should spec export the reserved name and refusal text for the runtime to reuse?",
"options": [
"A: export the name constant and the sentence builder from @objectstack/spec, and have AppPlugin and the admin service import them (runtime + service-datasource edits, an api-surface addition)",
"B: keep three synonymous sentences, each owned by its door"
],
"recommendation": "A, as a follow-up in the runtime lane. Long-term fit: one source for one contract. AI-proofing: no drift between the doors. Startup focus: small, but it adds a public export, so it needs its own card and not this PR's surface."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed: the published JSON Schema stays wider than the parse for this rule (it is recorded in the dropped-refinements ledger, like the 759 sites before it); closing it needs a new refinement-projection arm, which that module calls a public-contract decision"
]
}
objectstack-fleet commentedon Oct 16, 2026
Landing record, PR #23757 ·
domain:specseat 2 (#18549) · sessionsession_01LFqdRsuM5nkqoJdHd5ztVk· 2026-10-16T01:28Zorigin/main0a052e6705"feat(spec)!: a stack's datasources collection refuses a datasource named default, the host-reserved name (#23757)", merged through the queue at 2026-10-16T01:27Z (git branch -r --contains→origin/main)gh-readonly-queue/main/*ref names #23757aad16cefdc4cequalsgit merge-tree --write-treeof its parenteae3b389d2witha6f3e6e812. That head carries contract review PASS6205386930and ACCEPT6205431264.completedbyFixes #23634What ships. A stack's
datasourcescollection refuses an entry nameddefaultatdatasources.N.name, with the runtime's sentence and remedy (setOS_DATABASE_URL, or declare the datasource under another name and route objects to it). It reachesdefineStack,os validateand publish.DatasourceSchemaand the host's owndefaultrow are unchanged. The D3 entry isstack-datasource-host-reserved-name-refused.Follow-ups.
skills/objectstack-data/rules/datasources.md:4-6) is skills(objectstack-data):rules/datasources.mdreads as an invitation to declare a datasource nameddefault, which the stack now refuses as host-reserved #23768 (domain:skills, Tier H). ItsBlocked-by: #23757is now cleared.