Repository navigation
fix(objectql): an artifact hot reload refreshes every registry entry the boot registered, so an edited email template is what the next send renders - #27596
Conversation
…the boot registered The metadata:reloaded hook re-ingested objects only, so every other item the boot registered through the manifest service (email templates, flows, dashboards, pages, actions, ...) kept its boot copy in the SchemaRegistry. The list read and its execution face lay the registry over the metadata service, so sends and flow re-binds used the old definitions while the by-name read served the edit. Re-register each reloaded package the registry already holds through registerApp, under its installed source. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXny7xEWEwLyAqVpmq
…eset Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXny7xEWEwLyAqVpmq
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXny7xEWEwLyAqVpmq
…otype.at Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXny7xEWEwLyAqVpmq
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5e95115de695043487f1ff71b72c45332dbe6a67 && git checkout 5e95115de695043487f1ff71b72c45332dbe6a67
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 492703e14d50f9a604692519abb43025cd9c2ce1 7bb6d5326822ee50a5edbfa6553614e22e021579 && git checkout -B drift-repro 492703e14d50f9a604692519abb43025cd9c2ce1 && git merge --no-ff 7bb6d5326822ee50a5edbfa6553614e22e021579
node scripts/docs-audit/affected-docs.mjs --json 492703e14d50f9a604692519abb43025cd9c2ce1
|
…one owner, across an artifact reload Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgXny7xEWEwLyAqVpmq
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Readings for the seat's three questions on this PR, head 7bb6d53 (test-only push over ba97ffc). Proposed text for the body's
Runtime pin file: 4 passed (4); eslint and |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F 维护者速读(席位终稿)
席位意见: ACCEPT。修在根因(重载只重收 object,注册表其余类型停在启动副本),一处调用、一个私有方法,不碰公开面。 席位核对ACCEPT · head
|
Requested by Sales · project thread
Fixes #27488
Clause-②: no
Metadata-protocol: none
维护者速读
os dev热重载 artifact 后,引擎把它已装的包重新登记一遍,邮件模板、流程、仪表盘、页面、按钮等所有类型的列表读与发送读都跟上新版本。PUT /meta保存不受影响(有测试钉住);只刷新已装的包,禁用状态保留;回滚即还原packages/objectql/src/plugin.ts一处。packages/objectql/src/plugin.ts(refreshReloadedPackages与它在metadata:reloaded钩子里的调用)、packages/runtime/src/standalone-stack-artifact-reload-registry-refresh.test.ts、packages/plugins/plugin-email/src/template-artifact-reload.integration.test.ts。pnpm --filter @objectstack/runtime exec vitest run src/standalone-stack-artifact-reload-registry-refresh.test.ts,预期 3 条全绿;去掉钩子里那一行调用则第一条转红,列表读停在 v1。What changes
The boot registers an artifact twice: the artifact door (
MetadataPlugin) into the metadata service, andAppPlugininto the SchemaRegistry through themanifestservice. A hot reload (artifact-file watcher or HMR POST, both_reloadAndAnnounce) re-registered only the first, and ObjectQL'smetadata:reloadedhook re-ingested objects alone. The by-name read asks the metadata service first, so it served the edit; the list read and its execution face (getMetaItemsForExecution) lay the registry over the metadata service, so they kept the boot copy until a cold restart. The email plugin renders a send from that execution face, and the automation engine re-binds flows from it on the same reload.The hook now calls
refreshReloadedPackagesbefore the object ingest: each package body on the payload that the registry already holds (sourcecodeormarketplace) is re-registered throughregisterApp, the body door the boot used, under its installed source, withresolveArtifactPackageEntriesfor the order (the call themanifestservice makes).installPackagekeeps the lifecycle on a re-install, so a disabled package stays disabled. A package the boot never put in the registry is left to the artifact door as before (nothing of it is stale there). A body that fails to re-register is named in onewarnline and keeps its previous entries; the reload goes on.Scope (the card's first deliverable): measured on the real
createStandaloneStackcomposition, every registry-backed type was stale, not onlyemail_template. With each type at v1 at boot and v2 in the file, before the fix:email_template,dashboard,flow,page,actionobjectSo the fix refreshes every collection
registerAppdecomposes. That changes no semantics for the other types: it makes their list and execution reads agree with the by-name read, the same defect. The flow re-bind on reload (service-automation'smetadata:reloadedre-sync) now binds the edited flow, which is what that re-sync was written for.File surface:
packages/objectql/src/plugin.tsonly, plus a test inpackages/runtimeand one inpackages/plugins/plugin-email. Nopackages/spec, noobjectql/src/engine.ts. Branch is the card thread's harness-pinnedclaude/project-thread-mtq6wj(the claim is the identity record).Tests
packages/runtime/src/standalone-stack-artifact-reload-registry-refresh.test.ts(new): the realos devcomposition and the real reload body. Pins list, execution face and by-name for six types after one and two reloads (v1 positive control at boot = cold boot), the package record (source, enabled, status) across a reload, and that an environment save throughsaveMetaItemstill wins on all three reads after a reload re-registers the package beneath it.packages/plugins/plugin-email/src/template-artifact-reload.integration.test.ts(new): realObjectQLPlugin, the app registered through themanifestservice at init asAppPlugindoes, the artifact door's reload payload, and a realEmailServicesend over the loaderEmailServicePluginwires. The send renders the reloaded subject and the list serves it.Red before the fix: plugin-email
expected 'Task done (v1): Ship it' to be 'Task done (v2): Ship it'; runtime: the five types above listed v1 on both list faces.Acceptance notes
Verification
Ablation (fix commit 41d95a7): drop the refresh call, both pins go red; restore, both green
scripts/ablation-replace.mjs --holdreplacedthis.refreshReloadedPackages(ctx, payload);inpackages/objectql/src/plugin.tswith a marker statement (on-disk counts: marker 1, call 0), thenpnpm --filter @objectstack/objectql build, andscripts/ablation-dist-preflight.mjs objectql '__ABLATE_27488'found the marker indist/index.jsanddist/index.mjs(the JS reached dist; the DTS step reported the now-unused method, which does not change the JS the suites load).expected 'Task done (v1): Ship it' to be 'Task done (v2): Ship it'.1 failed | 1 passed (2)(the reload case; list and execution face back on v1).Restore:
--restoregave blob after restore == blob at HEAD (4aa979799172),git diff HEADempty; rebuilt,--absentpreflight: marker absent from all 14 built files, tree clean. Both pins green again (1 passed (1),2 passed (2)). The preservation pin for an environment save was added after the ablation; it is a keep-alone pin, green with the fix.Suites (after merging origin/main at 37e2caf, closure rebuilt)
pnpm --filter @objectstack/objectql test: Test Files 486 passed (486), Tests 9128 passed (9128).pnpm --filter @objectstack/plugin-email test: Test Files 45 passed (45), Tests 747 passed (747).pnpm --filter @objectstack/runtime exec vitest run src/standalone-stack-artifact-reload-registry-refresh.test.ts: 3 passed (3). The rest of the runtime suite is left to CI (Test Core): the diff adds one test file there and changes no runtime source.@objectstack/objectql,@objectstack/plugin-email(incl.check:test-typecheck),@objectstack/runtime: all exit 0.--no-inline-configon the three changed files: exit 0.Derived gates on head ba97ffc: 63 derived, 62 run (all exit 0), 1 NOT MEASURED
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this tree, every command run with its exit code captured before any pipe, then--ran: "63 derived famil(ies) accounted for — 62 run, 1 NOT-MEASURED".pnpm check:dual-build-cjs-loads, exit 3 (prerequisite: 35 workspace packages outside this diff's build closure have nodist/in this container). The diff changes no export and no package entry; CI'sLint & Repo Gatesruns it on a full build.check-plugin-teardown-shape --self-testneeded its pinned fixture commit fetched into this shallow clone, then 48 cases pass.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgUCizoYWQVJWjhdR6F