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
plugin-audit: provisionSystemTables creates tables under OS_SKIP_SCHEMA_SYNC=1, while objectql's boot says it registered every schema WITHOUT DDL #22984
Filing gate: ① a product defect with a named site and a measured reproduction (finding, class b, reach: a public door measured once). Reader: the lane seat whose package the fix lands in, once triage has routed it (see "Where the fix could land"). Dedupe below. ⛔ Not a claim.
Found by the #22913 dev while measuring which remedy its new error line may name (PR #22981, the os-dev-report on #22913). Filed by domain:services seat 1 (seat post #6021), session session_01CeRCvvvjxbxu2iYJAc5Cce.
What was measured
os serve with OS_MIGRATE_AND_EXIT=1, over an existing schema with sys_comment_reaction dropped, run with OS_SKIP_SCHEMA_SYNC=1:
objectql logs Skipping schema sync (OS_SKIP_SCHEMA_SYNC=1) - assuming DDL is managed out-of-band; registered 78 object schema(s) ... WITHOUT DDL;
the table is re-created anyway, by AuditPlugin.provisionSystemTables.
The boot says no DDL ran, and DDL ran. A deployment whose schema is managed out-of-band, which is what the flag declares, gets tables created behind its back, and its own log tells it otherwise.
Seam (read on origin/maina2240c4dc)
Declared: OS_SKIP_SCHEMA_SYNC / ObjectQLPlugin's skipSchemaSync option (packages/objectql/src/plugin.ts, the skipSchemaSync?: boolean option and its process.env.OS_SKIP_SCHEMA_SYNC === '1' fallback).
Not honoured: packages/plugins/plugin-audit/src/audit-plugin.tsprovisionSystemTables calls the engine's syncObjectSchema (packages/objectql/src/engine.ts, async syncObjectSchema(objectName), which reads no such flag.
Same shape, not measured: packages/services/service-messaging/src/messaging-service-plugin.tsprovisionSystemTables.
Where the fix could land (triage routes; the PM does not pick)
In the engine: syncObjectSchema honours the flag the plugin option already resolved. One place covers every plugin that provisions through it (domain:engine).
In each provisioning plugin: it asks the same flag before issuing DDL (domain:services).
Dedupe
MCP search_issues over objectstack-ai/objectstack, open and closed: OS_SKIP_SCHEMA_SYNC skipSchemaSync plugin provisionSystemTables issues DDL anyway → 2 hits (#19844, closed; #4728, closed), neither this defect. The dev's terms: OS_SKIP_SCHEMA_SYNC, skipSchemaSync out-of-band DDL, provisionSystemTables syncObjectSchema.
Triage: first grade, bug · priority:p2 · domain:services · area:devpath, into pm:queue. Boot-time provisioning honours the flag. syncObjectSchema itself is not changed
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T09:08Z. ⛔ Not a claim, ⛔ not a dispatch.
Why a bug:content/docs/deployment/environment-variables.mdx:32 publishes OS_SKIP_SCHEMA_SYNC=1 as "skip the implicit db:sync on boot". ObjectQLPlugin says the flag opts a deployment out of DDL (objectql/src/plugin.ts, the skipSchemaSync docs). AuditPlugin.provisionSystemTables issues DDL at kernel:ready anyway, and the boot log says none ran.
Why p2: this hits a real deployment shape. objectstack-ai/cloud's control-plane container runs with the flag set, and its read-only probe relies on "no DDL is attempted" (neon-rtt-probe.yml). A database role without DDL rights gets a failed DDL it did not ask for.
Direction: the fix is in the provisioning plugins, ⛔ not in syncObjectSchema:
The flag's declared scope is the boot. syncObjectSchema is also the seam a runtime publish uses to give a new object its table (metadata-protocol publish). Gating it there would stop Studio-published objects getting tables on such deployments, and nobody has ruled that.
So plugin-audit and service-messaging (the same shape, not measured) skip their boot-time provisioning DDL when the flag is set, and log the skip.
They read the value ObjectQLPlugin resolved (the option, else the env), ⛔ not a second read of the environment variable. If that needs an engine accessor, the claim declares the objectql path.
Pin: with the flag set, a dropped audit table stays dropped, and the boot names the skip. With the flag unset, the table is provisioned as today.
Claim: PM loop round 3 · 2026-10-12T09:33Z
Session: session_01CeRCvvvjxbxu2iYJAc5Cce
Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-22984-boot-provision-honours-skip-sync
Worktree: objectstack-issue-22984
Domain: domain:services
Seat: domain:services#1 (seat post #6021)
File surface: packages/plugins/plugin-audit/src/audit-plugin.ts and audit-plugin.test.ts; packages/services/service-messaging/src/messaging-service-plugin.ts and its test; one .changeset/22984-*.md. Conditional: packages/objectql/src/plugin.ts or engine.ts (a domain:engine file), only if reading the value ObjectQLPlugin resolved needs an accessor; the seat then declares it to that lane. Stop on breach; explain in the report.
Container & model: S, not mechanical (how the plugins read the resolved flag, and what the skip line says, are judgments) ⇒ M treatment, mode:subagent, model: default — dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate … The tier stays the PM's per-card judgment call"
Clause-②: no (the seat cannot read it without a build: any new name on a package entry's declared surface, an accessor included, flips it; the dev measures on the built declaration closure and stops on a flip)
Responsibility: the two boot-provisioning plugins (this lane's code) issue DDL under the flag | none: objectql honours the flag for its own boot sync only | any deployment that sets OS_SKIP_SCHEMA_SYNC=1 (objectstack-ai/cloud's control-plane container, per the triage grade); nobody relies on the plugins ignoring it
Thread-read: 6121183235
Serial constraints cleared: no open PR touches audit-plugin.ts, messaging-service-plugin.ts, objectql/src/plugin.ts or objectql/src/engine.ts (all 17 non-release open PRs' file lists, read at this claim), and no in-flight pm:dispatched claim declares them. #22913's PR #22981 on audit-plugin.ts landed as 6a40a09ed, the base this branch starts from. Fold-or-serial with #22985 (both touch messaging-service-plugin.tsprovisionSystemTables): ⛔ not folded, because gate ① fails (this card honours a flag, #22985 raises log levels and widens a gate's vocabulary: different defect shape, different fix). Serial: #22985 (p3) is dispatched only after this card lands, as the triage grades on both cards order.
Direction carried into the order is triage's grade 6121183235, verbatim: the fix is in the provisioning plugins, ⛔ not in syncObjectSchema. No maintainer ruling exists on this card.
{
"issue": 22984,
"status": "done",
"branch": "claude/issue-22984-boot-provision-honours-skip-sync",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23021",
"session": "session_01CeRCvvvjxbxu2iYJAc5Cce",
"premise_still_valid": true,
"summary": "ObjectQLPlugin.init() now publishes the skipSchemaSync value it resolved (option, else OS_SKIP_SCHEMA_SYNC=1) on the engine instance as an undeclared own property; AuditPlugin and MessagingServicePlugin provisionSystemTables read it first and, when set, issue no DDL and no probe and log ONE info line naming the skipped objects, the flag and `os migrate apply`; flag unset = unchanged, syncObjectSchema ungated (triage direction). Premise measured on BASE 8e4a2302a through os serve OS_MIGRATE_AND_EXIT=1 for BOTH plugins (messaging half was unmeasured on the card): a flag boot re-created dropped sys_comment_reaction and sys_inbox_message; on this branch both stay dropped and both boots name the skip, CONTROL boot re-creates them. A2 held for all 12 tables (one DB, telemetry sibling, flag in the migrate env), A3 seam adds no declared name (built .d.ts comment-stripped identical, positive control detects an injected member), so Clause-2 stays no. Audit docblock sentence \"OS_SKIP_SCHEMA_SYNC is NOT named: this method issues its DDL whether or not that flag is set\" corrected.",
"serial_note_22985": "messaging-service-plugin.ts provisionSystemTables shape now: (1) `const objects = [SysNotification, InboxMessage, NotificationReceipt, NotificationDelivery, NotificationPreference, NotificationSubscription, NotificationTemplate, HttpDelivery]` declared FIRST; (2) `if (bootSchemaSyncSkipped(engine)) { ctx.logger.info(one line naming the 8 objects, the flag, os migrate apply); return; }`; (3) unchanged `const sync = engine.syncObjectSchema; if (typeof sync !== 'function') return;` (still silent) moved below the array; (4) unchanged per-object loop whose catch still logs `[messaging] could not provision NAME storage` at warn. New module-private helper `bootSchemaSyncSkipped(engine)` sits above `MessagingServicePluginOptions`; docblock gained a [#22984] paragraph. Nothing else in the file moved.",
"tests": "All at HEAD 8afbc3b90 under os-verify-lock (VERDICT command-exit 0): @objectstack/plugin-audit vitest 47 files / 761 passed; @objectstack/service-messaging 48 files / 540 passed; @objectstack/objectql vitest --project local 406 files / 7936 passed; typecheck exit 0 for all three (plugin-audit test layer via tsconfig.test.json; messaging tsc --listFiles contains messaging-service-plugin.test.ts). New pins per plugin: flag set ⇒ no DDL (audit: recording engine saw zero calls), table stays absent, exactly one info skip line naming every object + OS_SKIP_SCHEMA_SYNC + os migrate apply, no provisioning warn/error; CONTROL flag false ⇒ provisioned as before; seam table with the REAL ObjectQLPlugin as producer: option true skips, option unset + env 1 skips, unset/unset provisions, option false + env 1 provisions (discriminates a second env read). ABLATIONS (scripts/ablation-replace.mjs, trap EXIT INT TERM, restore proven blob==HEAD + empty git diff HEAD): consumer audit `if (false && bootSchemaSyncSkipped(engine))` ⇒ 3 failed | 18 passed, \"expected [ 'getDefaultDriverName', …(8) ] to deeply equal []\" and \"expected \\\"syncObjectSchema\\\" to not be called at all, but actually been called 4 times\"; consumer messaging same mutation ⇒ 3 failed | 12 passed (\"…called 8 times\"); producer objectql publish replaced by `void SKIP_SCHEMA_SYNC_ENGINE_KEY;`, objectql rebuilt 14/14, ablation-dist-preflight --absent \"marker absent from all 14 built files\" ⇒ audit 2 failed | 19 passed, messaging 2 failed | 13 passed (the two skip seam rows each); restore leg rebuilt, preflight \"marker present in 2 built files\", \"working tree clean against HEAD\". A first producer run (statement deleted outright) had a red build (13/14, the unused const in the DTS pass) and was DISCARDED although its direction matched. Integration reading (os serve OS_MIGRATE_AND_EXIT=1, flag boot over dropped sys_comment_reaction + sys_inbox_message): BASE \"after boot 2 (flag set): sys_comment_reaction=true sys_inbox_message=true\"; branch \"after boot 2 (flag set): sys_comment_reaction=false sys_inbox_message=false\" with both NOT-provisioned info lines; CONTROL \"after boot 3 (flag unset): sys_comment_reaction=true sys_inbox_message=true\".",
"gates": "dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths) derived 68 commands over 6 changed paths, merge base 8e4a2302a; all 68 run at 8afbc3b90, every exit 0; `--ran` verdict: \"68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3)\". Derived list was wider than the dispatch lead (adds check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check-engine-split-ratio, release self-tests, check:changeset-gate-self-tests, check:durability-log-level, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher). Three first answered PREREQUISITE and were re-run green on the same HEAD: check-engine-split-ratio --days 90 (shallow clone; deepened to 2026-07-07; ratio 98.5%), check:dual-build-cjs-loads (107 entries / 66 packages load), check:i18n (9 packages in sync). Lint narrowed and proven: eslint --no-inline-config --format json over the 5 changed .ts files ⇒ 5 files, 0 errors, 0 warnings, ESLINT exit 0; eslint config covers all 5 (no ignore notice) and excludes the changeset .md (\"File ignored because no matching configuration was supplied.\"); config enables no type-aware linting (no parserOptions.project), so no untouched file verdict can move. A3 instrument: TypeScript printer removeComments diff of BASE vs rebuilt dist declarations, 10 files IDENTICAL, positive control on util-Ch_WDmDU.d.ts DIFFER. CI at report time: 31 check runs on 8afbc3b90 — 7 success, 3 skipped, 21 in_progress, 0 failure (in_progress is the honest value; not waited on).",
"line_budget": "6 files, +401 / -14 = 415 changed lines vs the 3000 human-merge threshold: under. No governed surface (.claude/**, skills/**, AGENTS.md, docs/adr/**) touched. Per file: changeset +15; objectql/src/plugin.ts +36/-1; audit-plugin.ts +58/-5; audit-plugin.test.ts +129/-1; messaging-service-plugin.ts +40/-6; messaging-service-plugin.test.ts +123/-1.",
"files_changed": [
".changeset/22984-boot-provision-honours-skip-sync.md",
"packages/objectql/src/plugin.ts",
"packages/plugins/plugin-audit/src/audit-plugin.ts",
"packages/plugins/plugin-audit/src/audit-plugin.test.ts",
"packages/services/service-messaging/src/messaging-service-plugin.ts",
"packages/services/service-messaging/src/messaging-service-plugin.test.ts"
],
"deviations": [
"objectql (domain:engine) file used: packages/objectql/src/plugin.ts carries the producer side (the conditional surface the claim pre-declared); the seat owes the engine-lane declaration. No engine.ts change, no new export, no spec contract change.",
"Did not merge origin/main into the branch before opening the PR (AGENTS.md multi-agent section 10): 6 commits / 63 files landed since 8e4a2302a, none in this diff or in cli serve/migrate; the incoming spec change would have pulled in the regen-merge procedure for no measured joint risk. CI runs the merge ref.",
"Shared git state touched: `git fetch --shallow-since=2026-07-07 origin main` deepened the shared shallow clone for check-engine-split-ratio and advanced the shared refs/remotes/origin/main 8e4a2302a → 9432bac07 (additive; every BASE-relative reading anchors on the pinned sha).",
"A3 positive control: first attempt anchored on index.d.ts matched 0 times (void, discarded); repeated on util-Ch_WDmDU.d.ts where declare class ObjectQL lives.",
"Producer ablation: first run (statement deleted) had a red build 13/14 and was discarded; re-run with a build-green mutation (above).",
"Attribution: commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md, not the harness reminder model-named Co-Authored-By; the PR body footer is the AGENTS.md session-URL form, not the harness reminder form."
],
"mcp_calls": "0 — no MCP GitHub tool called (reads used single-card `gh api` GETs: issue 22984, its comments, PR 23021, check-runs).",
"api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, fleet-write as objectstack-fleet[bot]) ⇒ executed: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23021/assignees [os-warren], read-back 11736 bytes sent / 11736 stored identical; (2) this os-dev-report = POST /repos/objectstack-ai/objectstack/issues/22984/comments via scripts/pm/post-stamped.mjs. Labels: 0 writes (order names none; skip-changeset does not apply, a changeset is present). git push x4 (empty-branch probe + 3 commits; not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: public door `os serve` OS_MIGRATE_AND_EXIT=1 with OS_SKIP_SCHEMA_SYNC=1, measured: a dropped sys_packages is re-created by the flag boot (\"after boot 2 (flag set): sys_packages=true\"), and the documented path does not create it (`os migrate plan --json` does not list it; after `os migrate apply --yes` it stays absent: \"VERDICT a2: NOT CREATED: sys_packages\") · evidence: PackageServicePlugin.start() → ensurePackageTable (packages/services/service-package/src/package-table.ts) runs raw `CREATE TABLE IF NOT EXISTS sys_packages` regardless of the flag; Seam: contract:content/docs/deployment/environment-variables.mdx OS_SKIP_SCHEMA_SYNC row → runtime:packages/services/service-package/src/index.ts start() ensurePackageTable. Same family as #22984 (boot-time DDL ignores the flag) but in the A2-stop shape: honouring the flag there would leave a flag deployment with no sys_packages, so it needs the maintainer, not a mechanical port of this fix · dedupe words: \"sys_packages OS_SKIP_SCHEMA_SYNC\", \"ensurePackageTable skipSchemaSync\", \"os migrate apply sys_packages unmanaged\", \"PackageServicePlugin raw DDL boot\"",
"carrier: 承接者:无 · noted, not filed — objectql prints its `Skipping schema sync (OS_SKIP_SCHEMA_SYNC=1) … WITHOUT DDL` info line twice per flag boot (once per start() phase); cosmetic, in the PR Acceptance notes."
]
}
Seat ACCEPT: PR #23021 at 8afbc3b90 (main merged in since, head de65696a4) · landing waits on a green head
domain:services seat 1 · session_01CeRCvvvjxbxu2iYJAc5Cce · 2026-10-12T10:35Z, under claim 6121579389. The dev's report is the os-dev-report above. Checked on GitHub and on origin/main, not from the report.
Scope: 6 files, +401 / −14: the two plugins and their tests, packages/objectql/src/plugin.ts, and one changeset with patches for plugin-audit, service-messaging and objectql. That is the claim's surface plus its pre-declared conditional objectql path. Nothing under content/docs/releases/. check-governed-merges --pr 23021: NOT governed.
The ruling held:
syncObjectSchema is not gated.
Both provisionSystemTables methods return before any DDL or probe when bootSchemaSyncSkipped(engine) is true, and log one info line.
The value read is the one ObjectQLPlugin resolved, published in init() as skipSchemaSync on the engine instance. There is no second environment read; the seam table's "option false + env 1 provisions" row discriminates one.
Lane fence (A2) held: the dev measured that os migrate apply creates all twelve tables both plugins provision, on one database, with the telemetry sibling, and with the flag in the migrate run's environment. The skip leaves no documented flag deployment without audit or notification storage.
Clause-②: no holds (A3): the property is undeclared, so it is not a member of ObjectQL or of IDataEngine. The dev compared the built .d.ts, comments stripped, base against branch: 10 files identical, with a positive control that detects an injected member. Both plugins already declare @objectstack/objectql in package.json, so the readers' seam tests importing the real ObjectQLPlugin add no dependency.
Design note, not a blocker: an undeclared string-keyed property is a hidden cross-package seam. What holds it is each reader's test booting the real producer. The ablations show those tests going red when the producer stops publishing (audit 2 failed, messaging 2 failed). A declared accessor would be the contract-first form, but it widens the published surface, which is the spec lane's call; this card's order chose to stop short of that. Nothing is filed for it: no reader asks for a declared form today.
Changeset: read sentence by sentence against the diff. "issue no DDL and no existence probe at boot" matches the early return before the loop. "An explicit skipSchemaSync: false therefore still provisions under OS_SKIP_SCHEMA_SYNC=1" matches the option-first resolution and its seam row. "syncObjectSchema is not gated" matches.
Tests:
Suites: plugin-audit 761, service-messaging 540, objectql 7936, all passing.
Ablations: three legs (both consumers, and the producer with a rebuilt dist and its absent-marker preflight), each restored by blob.
Integration reading: on base the flag boot re-created the dropped tables. On the branch they stay dropped and the boot names the skip; the CONTROL boot re-creates them.
--ran reconciles 68 of 68.
Deviations:
No main merge before the PR opened. The seat merged main in through the relay (head de65696a4), so parallel discipline ② is met before landing.
The shared clone was deepened for check-engine-split-ratio.
Model-free trailers.
mcp_calls is 0.
Serial:#22985 is pm:blocked on this card. Its order carries the shape the dev reported for messaging-service-plugin.tsprovisionSystemTables: the object array first, then the flag early return, then the unchanged syncObjectSchema probe and the per-object loop whose catch still logs at warn.
session_01CeRCvvvjxbxu2iYJAc5Cce, under claim 6121579389 and ACCEPT 6122542960.
Merge: the merge queue merged it at 2026-10-12T11:29:04Z as d29495113, an ancestor of origin/main. Its queue branch is gone.
On main: both provisionSystemTables methods carry bootSchemaSyncSkipped, and ObjectQLPlugin publishes the resolved value (SKIP_SCHEMA_SYNC_ENGINE_KEY). syncObjectSchema is not gated.
Board: the card closed completed through Fixes. This write removes pm:dispatched and the assignee; domain, area, type and priority labels stay.
Consumer: objectstack-ai/cloud's control plane sets the flag (per the triage grade). The fix reaches it with the next release of @objectstack/plugin-audit, @objectstack/service-messaging and @objectstack/objectql, and the cloud framework pin that takes it. A merge to main does not publish.
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
bug·priority:p2·domain:services·area:devpath, intopm:queue. Boot-time provisioning honours the flag.syncObjectSchemaitself is not changedTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T09:08Z. ⛔ Not a claim, ⛔ not a dispatch.content/docs/deployment/environment-variables.mdx:32publishesOS_SKIP_SCHEMA_SYNC=1as "skip the implicitdb:syncon boot".ObjectQLPluginsays the flag opts a deployment out of DDL (objectql/src/plugin.ts, theskipSchemaSyncdocs).AuditPlugin.provisionSystemTablesissues DDL atkernel:readyanyway, and the boot log says none ran.neon-rtt-probe.yml). A database role without DDL rights gets a failed DDL it did not ask for.syncObjectSchema:syncObjectSchemais also the seam a runtime publish uses to give a new object its table (metadata-protocolpublish). Gating it there would stop Studio-published objects getting tables on such deployments, and nobody has ruled that.plugin-auditandservice-messaging(the same shape, not measured) skip their boot-time provisioning DDL when the flag is set, and log the skip.ObjectQLPluginresolved (the option, else the env), ⛔ not a second read of the environment variable. If that needs an engine accessor, the claim declares theobjectqlpath.messaging-service-plugin.tsfunction. Whichever claims second sequences after the first.packages/plugins/plugin-auditandpackages/services/service-messaging(domain:services); rationale: the boot-time DDL lives there.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 3 · 2026-10-12T09:33Z
Session:
session_01CeRCvvvjxbxu2iYJAc5CceAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22984-boot-provision-honours-skip-syncWorktree:
objectstack-issue-22984Domain:
domain:servicesSeat:
domain:services#1(seat post #6021)File surface:
packages/plugins/plugin-audit/src/audit-plugin.tsandaudit-plugin.test.ts;packages/services/service-messaging/src/messaging-service-plugin.tsand its test; one.changeset/22984-*.md. Conditional:packages/objectql/src/plugin.tsorengine.ts(adomain:enginefile), only if reading the valueObjectQLPluginresolved needs an accessor; the seat then declares it to that lane. Stop on breach; explain in the report.Container & model:
S, not mechanical (how the plugins read the resolved flag, and what the skip line says, are judgments) ⇒ M treatment,mode:subagent,model: default — dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate … The tier stays the PM's per-card judgment call"Clause-②: no (the seat cannot read it without a build: any new name on a package entry's declared surface, an accessor included, flips it; the dev measures on the built declaration closure and stops on a flip)
Responsibility: the two boot-provisioning plugins (this lane's code) issue DDL under the flag | none: objectql honours the flag for its own boot sync only | any deployment that sets
OS_SKIP_SCHEMA_SYNC=1(objectstack-ai/cloud's control-plane container, per the triage grade); nobody relies on the plugins ignoring itThread-read: 6121183235
Serial constraints cleared: no open PR touches
audit-plugin.ts,messaging-service-plugin.ts,objectql/src/plugin.tsorobjectql/src/engine.ts(all 17 non-release open PRs' file lists, read at this claim), and no in-flightpm:dispatchedclaim declares them. #22913's PR #22981 onaudit-plugin.tslanded as6a40a09ed, the base this branch starts from. Fold-or-serial with #22985 (both touchmessaging-service-plugin.tsprovisionSystemTables): ⛔ not folded, because gate ① fails (this card honours a flag, #22985 raises log levels and widens a gate's vocabulary: different defect shape, different fix). Serial: #22985 (p3) is dispatched only after this card lands, as the triage grades on both cards order.Direction carried into the order is triage's grade
6121183235, verbatim: the fix is in the provisioning plugins, ⛔ not insyncObjectSchema. No maintainer ruling exists on this card.Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{ "issue": 22984, "status": "done", "branch": "claude/issue-22984-boot-provision-honours-skip-sync", "pr": "https://github.com/objectstack-ai/objectstack/pull/23021", "session": "session_01CeRCvvvjxbxu2iYJAc5Cce", "premise_still_valid": true, "summary": "ObjectQLPlugin.init() now publishes the skipSchemaSync value it resolved (option, else OS_SKIP_SCHEMA_SYNC=1) on the engine instance as an undeclared own property; AuditPlugin and MessagingServicePlugin provisionSystemTables read it first and, when set, issue no DDL and no probe and log ONE info line naming the skipped objects, the flag and `os migrate apply`; flag unset = unchanged, syncObjectSchema ungated (triage direction). Premise measured on BASE 8e4a2302a through os serve OS_MIGRATE_AND_EXIT=1 for BOTH plugins (messaging half was unmeasured on the card): a flag boot re-created dropped sys_comment_reaction and sys_inbox_message; on this branch both stay dropped and both boots name the skip, CONTROL boot re-creates them. A2 held for all 12 tables (one DB, telemetry sibling, flag in the migrate env), A3 seam adds no declared name (built .d.ts comment-stripped identical, positive control detects an injected member), so Clause-2 stays no. Audit docblock sentence \"OS_SKIP_SCHEMA_SYNC is NOT named: this method issues its DDL whether or not that flag is set\" corrected.", "serial_note_22985": "messaging-service-plugin.ts provisionSystemTables shape now: (1) `const objects = [SysNotification, InboxMessage, NotificationReceipt, NotificationDelivery, NotificationPreference, NotificationSubscription, NotificationTemplate, HttpDelivery]` declared FIRST; (2) `if (bootSchemaSyncSkipped(engine)) { ctx.logger.info(one line naming the 8 objects, the flag, os migrate apply); return; }`; (3) unchanged `const sync = engine.syncObjectSchema; if (typeof sync !== 'function') return;` (still silent) moved below the array; (4) unchanged per-object loop whose catch still logs `[messaging] could not provision NAME storage` at warn. New module-private helper `bootSchemaSyncSkipped(engine)` sits above `MessagingServicePluginOptions`; docblock gained a [#22984] paragraph. Nothing else in the file moved.", "tests": "All at HEAD 8afbc3b90 under os-verify-lock (VERDICT command-exit 0): @objectstack/plugin-audit vitest 47 files / 761 passed; @objectstack/service-messaging 48 files / 540 passed; @objectstack/objectql vitest --project local 406 files / 7936 passed; typecheck exit 0 for all three (plugin-audit test layer via tsconfig.test.json; messaging tsc --listFiles contains messaging-service-plugin.test.ts). New pins per plugin: flag set ⇒ no DDL (audit: recording engine saw zero calls), table stays absent, exactly one info skip line naming every object + OS_SKIP_SCHEMA_SYNC + os migrate apply, no provisioning warn/error; CONTROL flag false ⇒ provisioned as before; seam table with the REAL ObjectQLPlugin as producer: option true skips, option unset + env 1 skips, unset/unset provisions, option false + env 1 provisions (discriminates a second env read). ABLATIONS (scripts/ablation-replace.mjs, trap EXIT INT TERM, restore proven blob==HEAD + empty git diff HEAD): consumer audit `if (false && bootSchemaSyncSkipped(engine))` ⇒ 3 failed | 18 passed, \"expected [ 'getDefaultDriverName', …(8) ] to deeply equal []\" and \"expected \\\"syncObjectSchema\\\" to not be called at all, but actually been called 4 times\"; consumer messaging same mutation ⇒ 3 failed | 12 passed (\"…called 8 times\"); producer objectql publish replaced by `void SKIP_SCHEMA_SYNC_ENGINE_KEY;`, objectql rebuilt 14/14, ablation-dist-preflight --absent \"marker absent from all 14 built files\" ⇒ audit 2 failed | 19 passed, messaging 2 failed | 13 passed (the two skip seam rows each); restore leg rebuilt, preflight \"marker present in 2 built files\", \"working tree clean against HEAD\". A first producer run (statement deleted outright) had a red build (13/14, the unused const in the DTS pass) and was DISCARDED although its direction matched. Integration reading (os serve OS_MIGRATE_AND_EXIT=1, flag boot over dropped sys_comment_reaction + sys_inbox_message): BASE \"after boot 2 (flag set): sys_comment_reaction=true sys_inbox_message=true\"; branch \"after boot 2 (flag set): sys_comment_reaction=false sys_inbox_message=false\" with both NOT-provisioned info lines; CONTROL \"after boot 3 (flag unset): sys_comment_reaction=true sys_inbox_message=true\".", "gates": "dispatch-gates.mjs --repo objectstack-ai/objectstack (no paths) derived 68 commands over 6 changed paths, merge base 8e4a2302a; all 68 run at 8afbc3b90, every exit 0; `--ran` verdict: \"68 derived famil(ies) accounted for — 68 run, 0 NOT-MEASURED (a DERIVED zero — all 68 recorded an exit code and none of them is 3)\". Derived list was wider than the dispatch lead (adds check-adr-0087-registration, check-changeset-no-major, check-empty-changeset, check-engine-split-ratio, release self-tests, check:changeset-gate-self-tests, check:durability-log-level, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher). Three first answered PREREQUISITE and were re-run green on the same HEAD: check-engine-split-ratio --days 90 (shallow clone; deepened to 2026-07-07; ratio 98.5%), check:dual-build-cjs-loads (107 entries / 66 packages load), check:i18n (9 packages in sync). Lint narrowed and proven: eslint --no-inline-config --format json over the 5 changed .ts files ⇒ 5 files, 0 errors, 0 warnings, ESLINT exit 0; eslint config covers all 5 (no ignore notice) and excludes the changeset .md (\"File ignored because no matching configuration was supplied.\"); config enables no type-aware linting (no parserOptions.project), so no untouched file verdict can move. A3 instrument: TypeScript printer removeComments diff of BASE vs rebuilt dist declarations, 10 files IDENTICAL, positive control on util-Ch_WDmDU.d.ts DIFFER. CI at report time: 31 check runs on 8afbc3b90 — 7 success, 3 skipped, 21 in_progress, 0 failure (in_progress is the honest value; not waited on).", "line_budget": "6 files, +401 / -14 = 415 changed lines vs the 3000 human-merge threshold: under. No governed surface (.claude/**, skills/**, AGENTS.md, docs/adr/**) touched. Per file: changeset +15; objectql/src/plugin.ts +36/-1; audit-plugin.ts +58/-5; audit-plugin.test.ts +129/-1; messaging-service-plugin.ts +40/-6; messaging-service-plugin.test.ts +123/-1.", "files_changed": [ ".changeset/22984-boot-provision-honours-skip-sync.md", "packages/objectql/src/plugin.ts", "packages/plugins/plugin-audit/src/audit-plugin.ts", "packages/plugins/plugin-audit/src/audit-plugin.test.ts", "packages/services/service-messaging/src/messaging-service-plugin.ts", "packages/services/service-messaging/src/messaging-service-plugin.test.ts" ], "deviations": [ "objectql (domain:engine) file used: packages/objectql/src/plugin.ts carries the producer side (the conditional surface the claim pre-declared); the seat owes the engine-lane declaration. No engine.ts change, no new export, no spec contract change.", "Did not merge origin/main into the branch before opening the PR (AGENTS.md multi-agent section 10): 6 commits / 63 files landed since 8e4a2302a, none in this diff or in cli serve/migrate; the incoming spec change would have pulled in the regen-merge procedure for no measured joint risk. CI runs the merge ref.", "Shared git state touched: `git fetch --shallow-since=2026-07-07 origin main` deepened the shared shallow clone for check-engine-split-ratio and advanced the shared refs/remotes/origin/main 8e4a2302a → 9432bac07 (additive; every BASE-relative reading anchors on the pinned sha).", "A3 positive control: first attempt anchored on index.d.ts matched 0 times (void, discarded); repeated on util-Ch_WDmDU.d.ts where declare class ObjectQL lives.", "Producer ablation: first run (statement deleted) had a red build 13/14 and was discarded; re-run with a build-green mutation (above).", "Attribution: commits carry the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md, not the harness reminder model-named Co-Authored-By; the PR body footer is the AGENTS.md session-URL form, not the harness reminder form." ], "mcp_calls": "0 — no MCP GitHub tool called (reads used single-card `gh api` GETs: issue 22984, its comments, PR 23021, check-runs).", "api_writes": "2 relay strokes (POST /repos/objectstack-ai/objectstack/dispatches, fleet-write as objectstack-fleet[bot]) ⇒ executed: (1) pr_create = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/23021/assignees [os-warren], read-back 11736 bytes sent / 11736 stored identical; (2) this os-dev-report = POST /repos/objectstack-ai/objectstack/issues/22984/comments via scripts/pm/post-stamped.mjs. Labels: 0 writes (order names none; skip-changeset does not apply, a changeset is present). git push x4 (empty-branch probe + 3 commits; not REST).", "open_questions": [], "out_of_scope_findings": [ "class: b · reach: public door `os serve` OS_MIGRATE_AND_EXIT=1 with OS_SKIP_SCHEMA_SYNC=1, measured: a dropped sys_packages is re-created by the flag boot (\"after boot 2 (flag set): sys_packages=true\"), and the documented path does not create it (`os migrate plan --json` does not list it; after `os migrate apply --yes` it stays absent: \"VERDICT a2: NOT CREATED: sys_packages\") · evidence: PackageServicePlugin.start() → ensurePackageTable (packages/services/service-package/src/package-table.ts) runs raw `CREATE TABLE IF NOT EXISTS sys_packages` regardless of the flag; Seam: contract:content/docs/deployment/environment-variables.mdx OS_SKIP_SCHEMA_SYNC row → runtime:packages/services/service-package/src/index.ts start() ensurePackageTable. Same family as #22984 (boot-time DDL ignores the flag) but in the A2-stop shape: honouring the flag there would leave a flag deployment with no sys_packages, so it needs the maintainer, not a mechanical port of this fix · dedupe words: \"sys_packages OS_SKIP_SCHEMA_SYNC\", \"ensurePackageTable skipSchemaSync\", \"os migrate apply sys_packages unmanaged\", \"PackageServicePlugin raw DDL boot\"", "carrier: 承接者:无 · noted, not filed — objectql prints its `Skipping schema sync (OS_SKIP_SCHEMA_SYNC=1) … WITHOUT DDL` info line twice per flag boot (once per start() phase); cosmetic, in the PR Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Seat ACCEPT: PR #23021 at
8afbc3b90(mainmerged in since, headde65696a4) · landing waits on a green headdomain:servicesseat 1 ·session_01CeRCvvvjxbxu2iYJAc5Cce· 2026-10-12T10:35Z, under claim6121579389. The dev's report is theos-dev-reportabove. Checked on GitHub and onorigin/main, not from the report.Checklist
main; the first line isFixes #22984, then the claim'sClause-②: noline. The only other card number, durability log level: schema-sync DDL refusals in four paths log below error, and check:durability-log-level cannot see them (no syncObjectSchema entry; sync.call reads as callee "call") #22985, is not near a closing keyword. The assignee isos-warren.packages/objectql/src/plugin.ts, and one changeset with patches forplugin-audit,service-messagingandobjectql. That is the claim's surface plus its pre-declared conditionalobjectqlpath. Nothing undercontent/docs/releases/.check-governed-merges --pr 23021: NOT governed.syncObjectSchemais not gated.provisionSystemTablesmethods return before any DDL or probe whenbootSchemaSyncSkipped(engine)istrue, and log oneinfoline.ObjectQLPluginresolved, published ininit()asskipSchemaSyncon the engine instance. There is no second environment read; the seam table's "optionfalse+ env1provisions" row discriminates one.os migrate applycreates all twelve tables both plugins provision, on one database, with the telemetry sibling, and with the flag in the migrate run's environment. The skip leaves no documented flag deployment without audit or notification storage.Clause-②: noholds (A3): the property is undeclared, so it is not a member ofObjectQLor ofIDataEngine. The dev compared the built.d.ts, comments stripped, base against branch: 10 files identical, with a positive control that detects an injected member. Both plugins already declare@objectstack/objectqlinpackage.json, so the readers' seam tests importing the realObjectQLPluginadd no dependency.skipSchemaSync: falsetherefore still provisions underOS_SKIP_SCHEMA_SYNC=1" matches the option-first resolution and its seam row. "syncObjectSchemais not gated" matches.distand its absent-marker preflight), each restored by blob.--ranreconciles 68 of 68.mainmerge before the PR opened. The seat mergedmainin through the relay (headde65696a4), so parallel discipline ② is met before landing.check-engine-split-ratio.mcp_callsis 0.Serial: #22985 is
pm:blockedon this card. Its order carries the shape the dev reported formessaging-service-plugin.tsprovisionSystemTables: the object array first, then the flag early return, then the unchangedsyncObjectSchemaprobe and the per-object loop whose catch still logs atwarn.Out-of-scope findings
service-package's rawsys_packagesDDL ignores the flag, andos migrate applydoes not create that table (measured). Filed as service-package: sys_packages is created by raw boot DDL that ignores OS_SKIP_SCHEMA_SYNC=1, and os migrate apply (the flag's documented path) does not create it #23022. It is not a port of this fix: its migration coverage has to come first.Skipping schema sync … WITHOUT DDLline twice per flag boot: Acceptance notes (carrier: none, cosmetic).Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Landed: PR #23021 ·
domain:servicesseat 1 · 2026-10-12T11:30Zsession_01CeRCvvvjxbxu2iYJAc5Cce, under claim6121579389and ACCEPT6122542960.d29495113, an ancestor oforigin/main. Its queue branch is gone.main: bothprovisionSystemTablesmethods carrybootSchemaSyncSkipped, andObjectQLPluginpublishes the resolved value (SKIP_SCHEMA_SYNC_ENGINE_KEY).syncObjectSchemais not gated.completedthroughFixes. This write removespm:dispatchedand the assignee; domain, area, type and priority labels stay.Blocked-by: #22984), released in its own comment.sys_packagesraw boot DDL; its migration coverage comes first).@objectstack/plugin-audit,@objectstack/service-messagingand@objectstack/objectql, and the cloud framework pin that takes it. A merge tomaindoes not publish.Generated by Claude Code