Repository navigation
[finding] os migrate plan on examples/app-crm runs the app's onEnable hook, which reads sys_position / sys_permission_set the plan never declares: 6 DATABASE_ERROR + 6 WARN lines on every plan #21054
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p3·domain:cli·area:devpath·pm:queue. Direction:os migrate plan's declaration boot does not run app lifecycle hooksTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T04:32Z. ⛔ Not a claim, ⛔ not a dispatch.Why p3. It is noise, not damage: 6
DATABASE_ERRORand 6WARNlines on every plan ofexamples/app-crm. But a plan that runs an app'sonEnableside effects is the wrong shape for a read-only planning boot.Direction:
- The plan's declaration boot reads declarations and does not fire
runtime.onEnableorkernel:bootstrappedapp hooks. That fixes it for every app at one point. ⛔ It is not fixed by teaching app-crm's hook to guard its reads, although the hook may also guard. - If the plan needs a hook for declarations, the claim stops and reports which one.
- Pin: the plan on app-crm prints no
DATABASE_ERRORlines. - Neighbour: [finding]
os migrate planagainst a database that does not exist yet prints 6[sql-driver] DATABASE_ERROR … no such tablewarnings: the dry run defers the DDL, then its boot readssys_metadata,sys_metadata_activationandsys_migrationanyway #20821 (pm:dispatched, the plan's deferred-DDL lines) is the same command. The dispatcher's in-flight check reads its surface.
Generated by Claude Code
- The plan's declaration boot reads declarations and does not fire
- addedarea:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingSomething isn't working
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 of the
domain:cliseat's sessionsession_01VvcEokUG1tvVxkceYfR5XB(batch3):priority:p3, triage's first grade5924795251, filling a free slot under the maintainer's 「并发保持3」
Session:session_01VvcEokUG1tvVxkceYfR5XB
Account:huangyiirene
Branch:claude/issue-21054-plan-no-app-hooks
Worktree:objectstack-issue-21054
Domain:domain:cli
Seat:domain:cli#1
File surface:- The plan's declaration boot:
packages/cli/src/utils/schema-migrate.ts(the boot around:355–:395on9c8b65aa23) andpackages/cli/src/utils/schema-migration-plugins.ts(composeForDeclarations,:244, and its phase handling).- The declaration boot does not fire an app's
runtime.onEnableorkernel:bootstrapped/kernel:listeningapp hooks. - It is fixed at that one point, for every app.
- The declaration boot does not fire an app's
packages/runtime/src/app-plugin.ts: only if theonEnableexecutor itself must read a declaration-boot signal to stand down. Otherwise it is read only.- Pins beside the CLI's existing declaration-boot suites (
schema-migration-plugins.declaration-boot-write-guard.test.ts,schema-migrate.host-composition.integration.test.ts):- the plan on
examples/app-crmprints 0DATABASE_ERRORlines and 0 position-binding warnings, on a migrated file and on an absent one; - a hook registered from
init()is not fired on a plan (the control:apply's confirmed work after the boot is unchanged).
- the plan on
- One
.changeset/21054-*.mdfor@objectstack/cli(and@objectstack/runtimeif touched). - ⛔ Not here: teaching app-crm's hook to guard its reads as the fix (
examples/**read only). Also not here: the deferred-DDL reads ([finding]os migrate planagainst a database that does not exist yet prints 6[sql-driver] DATABASE_ERROR … no such tablewarnings: the dry run defers the DDL, then its boot readssys_metadata,sys_metadata_activationandsys_migrationanyway #20821, closed). - Stop clause: if the plan needs an app hook for its declarations, stop and report which one, per the grade.
(stop on a breach outside these; explain in the report)
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable).
Clause-②: no
Thread-read: 5924795251
Serial constraints cleared:#20821 (the same command, domain:engine) is closed. No open PR touches packages/cli/src/commands/migrate/**, packages/cli/src/utils/schema-migrat*, packages/runtime/src/app-plugin.ts or examples/app-crm/src/security/** (the file lists of all open PRs were read in this act, main 9c8b65aa23). In flight on this seat: #21002 (rest-server.ts published door, runtime domains/meta.ts) and #20800 (a merge of main into PR #21038). Both are disjoint. area:devpath: #20800 is in flight on a disjoint surface.
Generated by Claude Code
- The plan's declaration boot:
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21054, "status": "done", "branch": "claude/issue-21054-plan-no-app-hooks", "pr": "https://github.com/objectstack-ai/objectstack/pull/21138", "session": "session_01VvcEokUG1tvVxkceYfR5XB — mode:subagent, the PM's session id; this run is told apart by the branch", "premise_still_valid": true, "summary": "Reproduced the premise at origin/main 9c8b65aa23. On examples/app-crm, every plan and apply printed 6 DATABASE_ERROR lines (sys_position x3, sys_permission_set x3), 6 \"position binding lookup failed\" warnings and \"Executing runtime.onEnable\", on an absent file and on a file apply had just migrated. Host code reaches a declaration boot through two doors, and the fix closes each one where it enters, for every app. (1) The config's onEnable: AppPlugin gains a skipOnEnable option, beside skipSeedData. The migrate composition sets it on the app it builds, and start() withholds the hook, logs that it did, and exposes onEnableWithheld. A compiled artifact cannot carry onEnable, and a host AppPlugin is already wrapped. (2) A host plugin's init(): composeForDeclarations now forwards init with a context whose hook() does not register kernel:bootstrapped or kernel:listening, the two phases IPluginLifecycleEvents defines as work after registration ends. kernel:ready host hooks still run, and the write guard still refuses their row writes; kernel:shutdown and data hooks register as before. This repo's own plugins are untouched, so the value-shape gate announcement still prints. No in-repo plugin registers a post-declaration hook from init(): all 7 sites are in start(). So no hook the plan needs for its declarations was cut, and the stop clause did not fire. The plan's notes and --json composition.notes carry one line naming what was withheld. After the fix, app-crm prints 0 errors and 0 warnings on all 5 runs. The table list, the pending DDL, the drift and the rest of --json are identical. The triage direction reverses #13332's claim-time preference (keep log-only hooks running) for these two phases only. The existing pins were inverted in place, and the guard's phase-agnostic property stays pinned by an unwrapped writer.", "tests": "Repro: node packages/cli/bin/run.js migrate plan|apply in examples/app-crm, at base 9c8b65aa23 (dist built) and at fix af9ac5ded3 (runtime and cli rebuilt); table in repro. Suites at 3781713631 (pre-merge): runtime vitest --project local, 297 files, 4252 passed, 5 skipped. cli --project unit, 240 files, 3422 passed. cli --project integration over 11 migrate-related files (duplicates, meta.stored-flow-resolution, plan.deferred-reads, platform-migrations-arming, schema-migrate deferred-ddl/host-composition/integration/readonly-probe/teardown, write-guard, unmanaged-tables): 59 passed. After merging origin/main: at 5bd79b1b3c, runtime app-plugin.test.ts 35/35; cli schema-migration-plugins.test.ts 32/32; write-guard, host-composition and plan.deferred-reads 36/36; runtime and cli typecheck (with check:test-typecheck) exit 0. At 6d4ef7c9aa, host-composition 14/14 and cli typecheck exit 0. HEAD dc1c40ec39 differs by one comment line. New and changed pins: runtime skipOnEnable (withheld, logged, reported; bundle.default; no onEnable means nothing withheld). cli unit: the init context declines the 2 phases and forwards the rest; the composed app carries skipOnEnable. Write-guard file, real ObjectKernel: a POSITIVE CONTROL; THE FIX (host fires only kernel:ready, teardown kept, an unwrapped platform plugin keeps all 3 phases in the same boot); a host registering later from kernel:ready is declined; a new PHASE-AGNOSTIC guard pin with an unwrapped writer. Host-composition #21054 block: app-crm-shaped fixture, served-composition POSITIVE CONTROL, apply flush and coverage CONTROL, plan on migrated and absent files with 0 DATABASE_ERROR. Ablations, fix committed first, each through scripts/ablation-replace.mjs (anchor 1 to 0, blob changed; restore blob == HEAD, git diff HEAD empty). A1: POST_DECLARATION_PHASES emptied (blob 2f397895 to 1c1f8d3f). Unit red 2/32; integration red 9/34 (write-guard DEFECT, FIX and embedder; both #21054 kernel cases; #13332 FIX and R1; both #21054 plan cases, failing on DATABASE_ERROR sys_position from the host hook). Positive controls, the phase-agnostic pin and the apply control stayed green. A2: skipOnEnable true to false at the composition (blob 2f397895 to 7f29e92e). Red: unit compose case 1/32; both #21054 plan cases 2/34 (DATABASE_ERROR on sys_position and sys_permission_set from onEnable). A3: AppPlugin executor branch neutralised with planted marker __OS21054_ABLATION__ (blob b5996d12 to ab2c4acb); runtime rebuilt, ablation-dist-preflight found the marker in 2 built files (index.js, index.cjs). Red: runtime 2/35, cli unit 1/32, cli integration 2/34. Restore leg: rebuilt; preflight --absent found the marker in 0 of 6 built files, tree clean; runtime 35/35, unit 32/32, integration 34/34 green. Lint, a proven narrowing at dc1c40ec39: (1) population: eslint --print-config resolves a config for all 7 touched TS files, 5-6 rules each, no parserOptions.project; (2) count: --format json reports 7 files, 0 errors, 0 warnings; (3) invariance: eslint.config.mjs enables no type-aware linting, so this diff cannot move the verdict on any untouched file. Whole-repo pnpm lint left to CI.", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "3 — all through the fleet-write relay (scripts/pm), each a POST /repos/objectstack-ai/objectstack/dispatches that the board workflow executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, draft, relay run 36842002037, body read back identical (11632 bytes); (2) label-write --assign huangyiirene, POST /repos/objectstack-ai/objectstack/issues/21138/assignees, relay run 36842086967, read back matched; (3) this os-dev-report, POST /repos/objectstack-ai/objectstack/issues/21054/comments. git push (branch marker plus 7 pushes) is not a REST write.", "open_questions": [], "out_of_scope_findings": [], "gates": "At dc1c40ec39, node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 64 commands (same list at 5bd79b1b3c, 6d4ef7c9aa and dc1c40ec39). All 64 ran with exit codes recorded before any pipe, and all 64 exited 0. --ran: \"64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN\". Quoted verdicts: check-nul-bytes \"OK (scanned 9738 text file(s) ... no raw ASCII control bytes)\"; check-test-source-alias \"OK — 73 packages with tests scanned\"; check-i18n-coverage \"OK (13 config(s), 621 baselined untranslated string(s), none new)\"; check:cross-package-test-inputs \"OK: 30 package(s) read outside themselves, all declared\". The first union, at 5bd79b1b3c, had check:dual-build-cjs-loads and check:i18n-coverage at exit 3 (PREREQUISITE NOT MET: 9 packages had no dist); they were built. It also had check:test-source-alias at exit 1, a real finding in the new test (a dynamic import of @objectstack/runtime inside a test body), fixed in 6d4ef7c9aa. CI at dc1c40ec39, read once at report time: 31 check runs, 10 success, 3 skipped, 18 in_progress, 0 failure.", "deviations": [ "packages/runtime/src/app-plugin.ts was changed under the claim's condition. The onEnable executor must read the signal: it alone resolves the hook owner (bundle.default before the bundle). Stripping onEnable off a copy at the CLI would re-state that rule at the call site, and the boot would then log \"No runtime.onEnable function found\" about an app that has one. Precedent: the existing skipSeedData option for the same commands. The changeset covers @objectstack/runtime as a patch.", "The app-crm pin uses an app-crm-shaped fixture inside the test's tempdir, not examples/app-crm itself. A cli test reading examples/** is a cross-package test input, and declaring it would need edits to scripts/cross-package-test-inputs.mjs and turbo.json, outside the claim's file surface. The real app-crm is measured by the CLI repro (see repro).", "Existing #13332 pins changed in place, per the triage direction 5924795251. Host log-only and write hooks on kernel:bootstrapped/kernel:listening no longer run on a declaration boot (refusal counts 6 to 4 and 4 to 2 in the host-composition #13332 cases). Three write-guard residue pins moved their writer from bootstrapped/listening to kernel:ready, because those cases are about the guard, not the phase. A new pin keeps the guard's phase-agnostic property with an unwrapped writer.", "origin/main merged once (merge commit 5bd79b1b3c, main tip a11faeecb3; no conflict, no lockfile change); the 9 packages it touched were rebuilt. The full runtime and cli-unit suites ran at pre-merge 3781713631; after the merge, only the touched suites plus typecheck ran. The incoming commits touch cli and runtime but none of the files in this diff.", "The cli integration tier was not run in full: 11 migrate-related files ran, and the rest is declared to CI. Nightly-tier e2e files that spawn migrate (migrate-plan-exits, json-stdout-purity, migrate-apply-refuses-before-ddl, migrate-exit-code, migrate-unloadable-host-config-exit, config-miss-stdout-purity) are NOT MEASURED locally (reason: nightly tier, OS_TEST_TIERS). Plan --json stdout parsing on app-crm was measured in the repro.", "The gate union was re-run on the final head. The run at 5bd79b1b3c was superseded. A second loop at 6d4ef7c9aa was stopped by its recorded PID after one command, because a one-line comment fix (dc1c40ec39) followed; the reported union is the full run at dc1c40ec39." ], "files_changed": [ ".changeset/21054-plan-runs-no-app-hooks.md", "packages/cli/src/utils/schema-migrate.ts", "packages/cli/src/utils/schema-migration-plugins.ts", "packages/cli/src/utils/schema-migration-plugins.test.ts", "packages/cli/src/utils/schema-migration-plugins.declaration-boot-write-guard.test.ts", "packages/cli/src/utils/schema-migrate.host-composition.integration.test.ts", "packages/runtime/src/app-plugin.ts", "packages/runtime/src/app-plugin.test.ts" ], "repro": { "tree": "examples/app-crm, no dist artifact; node packages/cli/bin/run.js (dist); base origin/main 9c8b65aa23 built, fix af9ac5ded3 with runtime and cli rebuilt", "before": { "plan_absent": { "database_error_lines": 6, "position_binding_warnings": 6, "row_missing_warnings": 3, "paged_read_warns": 2, "onEnable_executed": true, "exit": 0 }, "plan_absent_json": { "database_error_lines": 6, "position_binding_warnings": 6, "onEnable_executed": true, "exit": 0 }, "apply_yes": { "database_error_lines": 6, "position_binding_warnings": 6, "onEnable_executed": true, "exit": 0 }, "plan_migrated": { "database_error_lines": 6, "position_binding_warnings": 6, "row_missing_warnings": 3, "paged_read_warns": 2, "onEnable_executed": true, "exit": 0 }, "plan_migrated_json": { "database_error_lines": 6, "position_binding_warnings": 6, "onEnable_executed": true, "exit": 0 } }, "after": { "plan_absent": { "database_error_lines": 0, "position_binding_warnings": 0, "row_missing_warnings": 0, "paged_read_warns": 0, "onEnable_executed": false, "exit": 0 }, "plan_absent_json": { "database_error_lines": 0, "position_binding_warnings": 0, "onEnable_executed": false, "exit": 0 }, "apply_yes": { "database_error_lines": 0, "position_binding_warnings": 0, "onEnable_executed": false, "exit": 0 }, "plan_migrated": { "database_error_lines": 0, "position_binding_warnings": 0, "row_missing_warnings": 0, "paged_read_warns": 0, "onEnable_executed": false, "exit": 0 }, "plan_migrated_json": { "database_error_lines": 0, "position_binding_warnings": 0, "onEnable_executed": false, "exit": 0 } }, "stderr_lines": "base 8 on each plan (6 DATABASE_ERROR plus 2 Paged-read warns); fix 0", "plan_output_identical": "Yes, apart from one added notes line. Non-log stdout diff = 1 line added, 0 removed, for plan absent, plan migrated and apply. --json is identical except composition.notes (2 to 3 entries): pending 15/15 (absent) and 0/0 (migrated), total 0/0, managedTables 15/15. \"Examined 15 managed table(s)\" on both. The absent file is not created on either side." } }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT: PR #21138 at
afc44ba5(os migrate plan/applyrun no apponEnableand no host post-declaration hooks on their declaration boot)domain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-01T10:03Z- Contract review of record:
5929160258on the PR,CONTRACT_REVIEW_TIER, headafc44ba5, PASS. It succeeds the FAIL5928867906ondc1c40ec, which failed on ② alone. - What the records found:
- Two doors, each closed where it enters, for every app:
- the config's
onEnableis withheld by theAppPluginexecutor under a newskipOnEnableoption, which only the migrate composition sets; - a host plugin's
init()gets a context that declineskernel:bootstrappedandkernel:listeningby phase.kernel:ready,kernel:shutdownand data hooks register as before.
- the config's
- Nothing else moves: every other
AppPluginconstruction path (serve,standalone-stack,plugin-dev,verify) takes the default. The write guard is untouched, and still refuses writes on every phase for unwrapped code. - Stop clause: it did not fire. All seven in-repo post-declaration hook sites are in
start(). - The
composeForDeclarationssuppresses onlystart(), so aninit()-registeredkernel:readyhook still writes duringos migrate plan— the guarantee holds only for hosts following an unwritten convention #13332 reversal (log-only host hooks no longer run on these two phases) is compelled by triage's grade5924795251. The record names it, and the overridden The declaration-boot write guard has two named coverage boundaries left — engine-held non-default drivers, and immediate DDL (dropTable/rotateShards) #14126 phase constraint, openly. - Measured on
examples/app-crm(the dev's repro): 6DATABASE_ERRORand 6 position-binding warnings per plan or apply before, 0 and 0 after, on five runs. The plan's tables, pending DDL and drift are identical.--jsongains one entry in the open-endedcomposition.notesarray.
- Two doors, each closed where it enters, for every app:
- The ② correction, in this window:
AppPluginis exported from@objectstack/runtime's root, and gains an optional constructor option and a public getter. UnderWHICH LEVELand the in-class precedents (skipSeedData,securityMetadataRegistrar), that isminorwithClause-②: yes (widening).- Commit
afc44ba5re-grades the changeset;@objectstack/clistayspatch. - The seat sent the matching PR body patch itself (line 2 and a
## Release gradingsection), because os-dev.md reserves post-create body edits for the seat. The read-back was identical.
- Seat verification on adoption:
git diff dc1c40ec afc44ba5is the changeset alone.- The export at
packages/runtime/src/index.ts:61and theminorgrading of the two sibling options (33a5ff499e) were checked. - Checks on
afc44ba5: 34 names, 29 success, 5 skipped, 0 red. git merge-treeagainstorigin/main(e952cff578) is clean.check-governed-merges --pr 21138: NOT governed, +913 / −60.
- Checklist:
- Draft, base
main, first lineFixes #21054,Clause-②: yes (widening). - 8 files:
packages/cli/src/utils/schema-migrat*with their pins,packages/runtime/src/app-plugin.tswith its pin (under the claim's condition, which the record judged met), and one changeset. examples/**untouched.
- Draft, base
- Out-of-scope findings: the dev reported none. The record's ③ notes carry no carrier, so they stay as Acceptance notes and nothing is filed:
POST_DECLARATION_PHASESis a list; a fourth post-declaration phase would need adding to it.- A host that registers through
getKernel()is outside the composition's reach. - The advisory docs-drift pages were not re-read.
- Next: readied and armed in this act. At the merge, the
Fixescloses this card, and the seat removespm:dispatched.
Generated by Claude Code
- Contract review of record:
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsLanded: PR #21138 →
f20f669e17(os migrate plan/applyrun no apponEnableand no host post-declaration hooks). The card is closed by theFixes, and the seat removespm:dispatcheddomain:cliseat ·session_01VvcEokUG1tvVxkceYfR5XB· 2026-10-01T10:25Z- Landing reading:
f20f669e17is onorigin/mainas a single-parent squash, 8 files, +913 / −60. Its diff equals the PR's net diff at headafc44ba5line for line, once theindexand hunk-header lines are set aside. - Content check, parent against squash:
skipOnEnableinpackages/runtime/src/app-plugin.ts: 0 lines, then 8.POST_DECLARATION_PHASESinpackages/cli/src/utils/schema-migration-plugins.ts: 0, then 6.
- Release input:
.changeset/21054-plan-runs-no-app-hooks.mdlands with@objectstack/runtimeminor,@objectstack/clipatchandClause-②: yes (widening), as the PASS record5929160258confirmed. - State: the merge closed this card as
completed.pm:dispatchedis removed in the same act as this note.domain:cli,area:devpath,bugandpriority:p3stay.
Generated by Claude Code
- Landing reading:
Filing gate: ① a defect with a named producer,
examples/app-crm/objectstack.config.tsonEnable→registerCrmPositionBindings(examples/app-crm/src/security/bind-position-sets.ts:111), which hookskernel:bootstrapped. Finding class (a).reach:the public CLI dooros migrate plan --database-url file:Xonexamples/app-crm, measured atorigin/mainb253fadfbby #20821's dev (os-dev-reporton #20821,out_of_scope_findings[0]).Filed by the
domain:engineexecution seat 2 (seat post #20966,session_01Ujdtvqs7ree7WyQmEDwEnG,os-litant). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens
Xis the fileos migrate apply --yeshas just migrated, so every table the plan manages exists. The plan's run then prints:[sql-driver] DATABASE_ERRORlines (sys_position×3,sys_permission_set×3) and 2 "Paged read … is NOT deterministic" warns;WARN [crm] position binding lookup failedlines and 3 "skipped (row missing)" lines.On an absent file, the same lines come on top of #20821's 6 deferred-DDL lines.
runtime.onEnable; stdout shows "Executing runtime.onEnable". The hook then reads plugin-security tables.applynever creates them, and the hook's reads are refused on every plan.Scope for whoever takes it (⛔ not a ruling)
migrate plan/apply) should not run hostonEnablehooks that read data; orapplycreates them; orexamples/app-crmis meant to boot with underos migrate.os migrate planon a migrated app-crm file prints 0DATABASE_ERRORlines and 0position binding lookup failedlines.Dedupe
mcp__github__search_issues, repo-scoped, open and closed: "migrate plan app-crm onEnable hook sys_position sys_permission_set DATABASE_ERROR position binding lookup failed" gave 2 hits, neither this door. #14846 (closed) is a plugin-auth test harness; #3728 (closed) is unique-index DDL visibility.