Skip to content

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

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcSwDXZrZg7nGjP6P3U

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:

At c4c515a, admin overlay saves through PUT /meta do reach sends immediately. This card is the source-edit plus hot-reload path.

Batch-of: 16 · QA run #27438

Symptom

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.

Reproduction (subject c4c515a)

  1. Copy examples/app-showcase to a scratch directory and boot it with os dev on its own port (watcher on, OS_PORT exported).
  2. With no en-US overlay row present, edit the subject of showcase_task_done_email in src/system/emails/index.ts.
  3. Run objectstack compile in the copy and wait for the boot log line artifact auto-reloaded (file watcher).
  4. GET /api/v1/meta/email_template/showcase_task_done_email serves the new subject.
  5. GET /api/v1/meta/email_template (the list) still serves the old subject.
  6. 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.
  7. 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.

QA-source: #27438 · integration-system.email-template-render · negative[0]


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    QA-source: #27438 · integration-system.email-template-render · negative[0]


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    • 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.
    • bug, pm:queue.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

    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)

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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"
      ]
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

    ACCEPT → PR #27596 comment 6500252655 · head 7bb6d532 · 2026-10-29T15:02Z · landing: pr_ready + automerge_enable sent (relay run 40897396257).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:automationbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions