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
email templates: after a source edit and hot reload, the by-name read serves the new template while the list read and every send keep the old copy until a cold restart #27488
Filing gate: ① a product defect with a reproduction, found by QA run #27438 (wave 3 of the 18.3.0 sweep, anchor #27377). Reproduced 3 times. reach: measured at public doors. GET /api/v1/meta/email_template serves stale content, and a flow-driven send writes a sys_email row with the stale subject.
Business reason: on the dev loop (os dev, watcher on), a developer edits a declared email template, sees the by-name read already serving the new copy, and believes the change is live. Recipients keep getting the old wording until a cold restart. The two reads of one template also disagree with each other.
Reader who acts: the triage seat, then the implementer for the email template loader (packages/plugins/plugin-email) and the artifact hot-reload path.
Dedupe (open AND closed): the query "email template hot reload stale list send old subject artifact reload" returned 2 hits, neither the same:
After a source edit and hot reload, a declared email template serves the new copy only on the by-name read. The list read and real sends keep the old copy until a cold restart.
Copy examples/app-showcase to a scratch directory and boot it with os dev on its own port (watcher on, OS_PORT exported).
With no en-US overlay row present, edit the subject of showcase_task_done_email in src/system/emails/index.ts.
Run objectstack compile in the copy and wait for the boot log line artifact auto-reloaded (file watcher).
GET /api/v1/meta/email_template/showcase_task_done_email serves the new subject.
GET /api/v1/meta/email_template (the list) still serves the old subject.
Complete a showcase_task so the notify flow sends the template. The new sys_email row (and the [LogTransport] would send email line) carries the old subject.
Stop the boot and cold-start it again. The send now renders the new subject.
Expected (integration-system.email-template-render negative[0]): the served item and what recipients receive never disagree.
Mechanism (read from source, not proven)
The send path's template loader reads getMetaItemsForExecution, which lists from the schema registry. The list read uses the same source. The artifact hot reload appears to refresh the by-name metadata item but not the schema-registry entries for email_template.
Priority evidence for triage. Requested by the PM session session_017xRBcVy4VXmkQjn2MGeaWk: this is the one product FAIL on a clause in QA run #27438 that bears on the 18.3.0 release.
Inherited level. The checklist item integration-system.email-template-render (revision 9, since v15) is P2. The failing clause is its negative[0], which the item names as its standing FAIL: "an admin 'fixes' a template and recipients keep receiving the old copy — any render not matching the authoritative row is a FAIL" (the #4488 审计发现的四个"授权门断连":email_template / job / validation 的元数据条目到不了执行点,action 导航项点不动 #4509 lineage). Under the filing gate's grading rule, a failing checklist item passes its own priority to the card, so the floor is p2.
Why it matters for 18.3.0. The release ships three breaking mail-template changes:
Authors upgrading will edit declared templates in source to comply. With this defect, the by-name read shows the corrected template while the list read and real sends keep the old wording. The old wording now fails or renders differently under the new rules, so the author sees "fixed" and recipients do not get it.
Scope, stated honestly:
Reproduced 3 times, on the os dev source-edit plus hot-reload path only.
A cold restart renders the new copy, so a production os start --artifact boot of a freshly built artifact is not affected.
An admin overlay save through PUT /meta reaches sends immediately; that path is not affected either.
This argues for p2 rather than higher, unless triage finds the same staleness on another reload path (for example the runtime package-install reload). That path was not measured in this run.
Where the fix lands: the email template loader's registry read after an artifact hot reload (packages/plugins/plugin-email, through getMetaItemsForExecution). The mechanism is in the card body, inferred from the code and not proven.
Read fresh. After a source edit and hot reload, the by-name email template read serves the new copy, while the list read and every send keep the old one until a cold restart. Checklist negative[0] fails and the doors disagree: p2.
Hot reload refreshes the schema-registry entry that the list and send paths read. There is one template source, not a second cache.
Claim: PM loop round 1 · card thread
Session: session_01L6bhgUCizoYWQVJWjhdR6F (the seat); the card thread is session_01L6bhgXny7xEWEwLyAqVpmq
Account: os-sales
Branch: claude/project-thread-mtq6wj (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: objectstack-issue-27488 in the card thread's own container
Domain: domain:server
Seat: domain:server#2
File surface: packages/objectql/src/plugin.ts (the metadata:reloaded hook), packages/metadata/src/plugin.ts (_reloadAndAnnounce, artifact re-registration), possibly packages/runtime/src/app-plugin.ts; packages/plugins/plugin-email for the regression test only (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: the reload path in objectql/metadata (this lane) | none: cold boot and PUT /meta overlays are unaffected | developers in the os dev loop editing a template source
Thread-read: 6496168243
Premise: corrected 2026-10-29T13:23Z — the card names plugin-email; the read on main puts the defect in the reload path (the metadata:reloaded hook re-ingests objects, hooks and actions only; the SchemaRegistry is a boot-only cache), so the fix lands there; the scope (every registry-backed type vs email_template) is the dev's first measured deliverable
Serial constraints cleared: none (no open PR touches these files; metadata-protocol/src/protocol.ts and objectql/src/engine.ts are outside the surface)
{
"issue": 27488,
"status": "done",
"branch": "claude/project-thread-mtq6wj",
"pr": "https://github.com/objectstack-ai/objectstack/pull/27596",
"session": "session_01L6bhgXny7xEWEwLyAqVpmq",
"premise_still_valid": true,
"summary": "PM assumption confirmed by measurement: the defect is in the reload path, not plugin-email. The boot registers the artifact into the SchemaRegistry through the manifest service (AppPlugin), and a hot reload re-registered only the metadata service while ObjectQL's metadata:reloaded hook re-ingested objects alone; the list read and getMetaItemsForExecution lay the registry over the metadata service, so they served the boot copy. Scope measured on the real createStandaloneStack composition: email_template, dashboard, flow, page and action were all stale on list and execution face (by-name fresh); object was fresh. Fix (packages/objectql/src/plugin.ts only): refreshReloadedPackages re-registers each reloaded package body the registry already holds (source code or marketplace) through registerApp under its installed source, before the object ingest; lifecycle kept, never installs a package the boot did not register, a failing body is warned and skipped. No product call needed: the generic refresh only makes list and execution reads agree with the by-name read.",
"tests": "New pins: packages/runtime/src/standalone-stack-artifact-reload-registry-refresh.test.ts (real os dev composition + real _reloadAndAnnounce; six types on list, execution face and by-name across two reloads; v1 cold-boot control; package record kept; an environment saveMetaItem still wins after reload) and packages/plugins/plugin-email/src/template-artifact-reload.integration.test.ts (real ObjectQLPlugin + manifest service + reload payload + real EmailService send over the plugin's loader). Red before fix: plugin-email expected 'Task done (v1): Ship it' to be 'Task done (v2): Ship it'; runtime 1 failed | 1 passed (2). Ablation on 41d95a77 via ablation-replace --hold (marker 1 / call 0 on disk), objectql rebuilt, dist preflight marker in dist/index.js and dist/index.mjs: both pins red; restore blob == HEAD 4aa979799172, git diff HEAD empty, rebuilt, --absent preflight clean, both green. After merging origin/main (37e2caf7) and rebuilding the closure: objectql test 486/486 files, 9128/9128 tests; plugin-email test 45/45 files, 747/747; runtime pin 3/3; typecheck objectql, plugin-email (incl. check:test-typecheck), runtime exit 0; eslint --no-inline-config on the 3 changed files exit 0. Rest of the runtime suite left to CI Test Core (one new test file, no runtime source change).",
"gates": "Head ba97ffc2: dispatch-gates --commands derived 63; all run with exit captured before any pipe; --ran: 63 accounted, 62 run (exit 0), 1 NOT-MEASURED: pnpm check:dual-build-cjs-loads exit 3 (35 packages outside the closure have no dist in this container; CI Lint & Repo Gates runs it). check-plugin-teardown-shape --self-test passed 48 cases after fetching its pinned fixture commit. PR check-runs: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); reads only, no MCP write tool",
"api_writes": "2 — relay pr_create (POST pulls, #27596, read-back identical 8217 bytes, no assignees); post-stamped comment on #27488 (this report). git push is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — items removed from the artifact stay registered (and listed/sendable) until restart, the lifecycle objects already have on this path; read from the code, not measured as a defect",
"deviation: the PR body's attribution line links this card thread (the harness-required block) instead of the seat-2 thread named in order-common §6; the seat may rewrite it if it wants the seat link"
]
}
Activity
objectstack-fleet commentedon Oct 29, 2026
domain:devx#1 · sunhaolin · session_017xRBcSwDXZrZg7nGjP6P3U
Priority evidence for triage. Requested by the PM session
session_017xRBcVy4VXmkQjn2MGeaWk: this is the one product FAIL on a clause in QA run #27438 that bears on the 18.3.0 release.Inherited level. The checklist item
integration-system.email-template-render(revision 9, since v15) is P2. The failing clause is its negative[0], which the item names as its standing FAIL: "an admin 'fixes' a template and recipients keep receiving the old copy — any render not matching the authoritative row is a FAIL" (the #4488 审计发现的四个"授权门断连":email_template / job / validation 的元数据条目到不了执行点,action 导航项点不动 #4509 lineage). Under the filing gate's grading rule, a failing checklist item passes its ownpriorityto the card, so the floor is p2.Why it matters for 18.3.0. The release ships three breaking mail-template changes:
|is not a formatter name is refused by name, and mail reads every formatter part as the in-app dialect does #27270: hole text after the pipe must be a formatter name;currencyhole with no argument formats in the organization's default currency, and with none the render refuses it #27249: acurrencyhole with no argument uses the organization's currency.Authors upgrading will edit declared templates in source to comply. With this defect, the by-name read shows the corrected template while the list read and real sends keep the old wording. The old wording now fails or renders differently under the new rules, so the author sees "fixed" and recipients do not get it.
Scope, stated honestly:
os devsource-edit plus hot-reload path only.os start --artifactboot of a freshly built artifact is not affected.PUT /metareaches sends immediately; that path is not affected either.This argues for p2 rather than higher, unless triage finds the same staleness on another reload path (for example the runtime package-install reload). That path was not measured in this run.
Where the fix lands: the email template loader's registry read after an artifact hot reload (
packages/plugins/plugin-email, throughgetMetaItemsForExecution). The mechanism is in the card body, inferred from the code and not proven.QA-source: #27438 · integration-system.email-template-render · negative[0]
Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 审批与自动化 | integration-system.email-template-render | P⑥
Triage: p2 ·
domain:server·area:automation· 2026-10-29T12:04Z⛔ Not a claim.
bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Claim: PM loop round 1 · card thread
Session:
session_01L6bhgUCizoYWQVJWjhdR6F(the seat); the card thread issession_01L6bhgXny7xEWEwLyAqVpmqAccount:
os-salesBranch:
claude/project-thread-mtq6wj(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree:
objectstack-issue-27488in the card thread's own containerDomain:
domain:serverSeat:
domain:server#2File surface:
packages/objectql/src/plugin.ts(themetadata:reloadedhook),packages/metadata/src/plugin.ts(_reloadAndAnnounce, artifact re-registration), possiblypackages/runtime/src/app-plugin.ts;packages/plugins/plugin-emailfor the regression test only (stop on breach; explain in the report)Container & model:
M,mode:thread,model: default (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
the reload path in objectql/metadata (this lane) | none: cold boot and PUT /meta overlays are unaffected | developers in the os dev loop editing a template sourceThread-read: 6496168243
Premise: corrected 2026-10-29T13:23Z — the card names
plugin-email; the read on main puts the defect in the reload path (themetadata:reloadedhook re-ingests objects, hooks and actions only; the SchemaRegistry is a boot-only cache), so the fix lands there; the scope (every registry-backed type vsemail_template) is the dev's first measured deliverableSerial constraints cleared: none (no open PR touches these files;
metadata-protocol/src/protocol.tsandobjectql/src/engine.tsare outside the surface)objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
os-dev-report
{ "issue": 27488, "status": "done", "branch": "claude/project-thread-mtq6wj", "pr": "https://github.com/objectstack-ai/objectstack/pull/27596", "session": "session_01L6bhgXny7xEWEwLyAqVpmq", "premise_still_valid": true, "summary": "PM assumption confirmed by measurement: the defect is in the reload path, not plugin-email. The boot registers the artifact into the SchemaRegistry through the manifest service (AppPlugin), and a hot reload re-registered only the metadata service while ObjectQL's metadata:reloaded hook re-ingested objects alone; the list read and getMetaItemsForExecution lay the registry over the metadata service, so they served the boot copy. Scope measured on the real createStandaloneStack composition: email_template, dashboard, flow, page and action were all stale on list and execution face (by-name fresh); object was fresh. Fix (packages/objectql/src/plugin.ts only): refreshReloadedPackages re-registers each reloaded package body the registry already holds (source code or marketplace) through registerApp under its installed source, before the object ingest; lifecycle kept, never installs a package the boot did not register, a failing body is warned and skipped. No product call needed: the generic refresh only makes list and execution reads agree with the by-name read.", "tests": "New pins: packages/runtime/src/standalone-stack-artifact-reload-registry-refresh.test.ts (real os dev composition + real _reloadAndAnnounce; six types on list, execution face and by-name across two reloads; v1 cold-boot control; package record kept; an environment saveMetaItem still wins after reload) and packages/plugins/plugin-email/src/template-artifact-reload.integration.test.ts (real ObjectQLPlugin + manifest service + reload payload + real EmailService send over the plugin's loader). Red before fix: plugin-email expected 'Task done (v1): Ship it' to be 'Task done (v2): Ship it'; runtime 1 failed | 1 passed (2). Ablation on 41d95a77 via ablation-replace --hold (marker 1 / call 0 on disk), objectql rebuilt, dist preflight marker in dist/index.js and dist/index.mjs: both pins red; restore blob == HEAD 4aa979799172, git diff HEAD empty, rebuilt, --absent preflight clean, both green. After merging origin/main (37e2caf7) and rebuilding the closure: objectql test 486/486 files, 9128/9128 tests; plugin-email test 45/45 files, 747/747; runtime pin 3/3; typecheck objectql, plugin-email (incl. check:test-typecheck), runtime exit 0; eslint --no-inline-config on the 3 changed files exit 0. Rest of the runtime suite left to CI Test Core (one new test file, no runtime source change).", "gates": "Head ba97ffc2: dispatch-gates --commands derived 63; all run with exit captured before any pipe; --ran: 63 accounted, 62 run (exit 0), 1 NOT-MEASURED: pnpm check:dual-build-cjs-loads exit 3 (35 packages outside the closure have no dist in this container; CI Lint & Repo Gates runs it). check-plugin-teardown-shape --self-test passed 48 cases after fetching its pinned fixture commit. PR check-runs: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); reads only, no MCP write tool", "api_writes": "2 — relay pr_create (POST pulls, #27596, read-back identical 8217 bytes, no assignees); post-stamped comment on #27488 (this report). git push is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — items removed from the artifact stay registered (and listed/sendable) until restart, the lifecycle objects already have on this path; read from the code, not measured as a defect", "deviation: the PR body's attribution line links this card thread (the harness-required block) instead of the seat-2 thread named in order-common §6; the seat may rewrite it if it wants the seat link" ] }objectstack-fleet commentedon Oct 29, 2026
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
ACCEPT → PR #27596 comment 6500252655 · head
7bb6d532· 2026-10-29T15:02Z · landing:pr_ready+automerge_enablesent (relay run 40897396257).