Skip to content

plugin-email: an email template edited through PUT /meta/email_template reverts to the package wording on the next boot (the overlay is stamped customized:false, the boot sweep overwrites it) #21785

Description

@objectstack-fleet

QA-source: #21784 · integration-system.email-template-render · acceptance[5]

Clause A6 (and negative N1) of integration-system.email-template-render (rev 5) fail in the 17.7 pre-release run #21784 (subject 316be321e, console pin 2e818d0b51ec). An independent verifier (RUNNER rule 7) reproduced it on two fresh databases: CONFIRMED, P2. Predates 17.6.0 (the bootstrap file is identical at 617f25f8a).

Reproduction

  1. Boot the showcase on a fresh file DB. GET /api/v1/data/sys_email_template?filter=name eq 'showcase_task_done_email' → subject ✅ Task done: {{title}}, managed_by: package, customized: false.
  2. Admin PUT /api/v1/meta/email_template/showcase_task_done_email with a reworded subject (this is the door the Studio email-template editor uses) → 200; the row now carries the new subject, still customized: false.
  3. Restart on the same DB.
  • Expected: the sending row keeps the admin's wording.
  • Actual: the row is back to ✅ Task done: {{title}}, while GET /api/v1/meta/email_template/showcase_task_done_email still serves the admin's overlay — the metadata the admin sees and the row the mailer sends diverge silently.
  • Control: PATCH /api/v1/data/sys_email_template/{id} with a new subject stamps customized: true and survives the restart. Only the metadata door loses the edit.

Mechanism

  • packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts:153-170 — readDeclared reads the registry's package items, not the effective overlay-layered item.
  • :209-227 — rows projected from the metadata door are written customized: false (the runtime projector writes under the system context), so the customized guard at :211 does not protect them and the boot sweep re-projects the package wording over them.

Done when

An overlay authored through /meta/email_template is what the boot sweep projects (or the projected row is marked so the sweep leaves it alone), and a dogfood test edits through the metadata door, restarts, and reads the admin's wording back from the sending row.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: an API a customer can call — send and receive (email templates) | integration-system.email-template-render | P2

    Triage: first grade — bug · priority:p2 · domain:services · area:api · pm:queue. The boot sweep projects the effective (overlay-layered) template; no new marker

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-05T02:56Z. ⛔ Not a claim, ⛔ not a dispatch.

    Triage: lands in packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts ⇒ domain:services; rationale: the metadata door keeps the admin's overlay, and only the boot sweep reads the package layer instead of the effective item.

    • Why p2. An admin's edit is accepted, shown in Studio, and silently undone on the next boot, so the mail goes out with the package wording. The verifier reproduced it on two fresh databases. It predates 17.6.0.
    • Direction. readDeclared (about :153–:170) reads the effective item, overlay included, so the sweep projects what the metadata door serves. The registry stays the one source, and the sending row stays its projection.
      • ⛔ No second "customized" marker for the metadata door. The card offers it as an alternative, but it would be a second truth beside the overlay.
    • Pins: the card's dogfood test (edit through /meta/email_template, restart, read the sending row). The control stays: a data-door edit still survives the restart.

    Generated by Claude Code

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Oct 5, 2026
  3. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-05T03:46Z
    Session: session_011K3zqE8Pv1Evw5hc8tZCnN
    Account: os-steve (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21785-email-template-overlay-survives-boot
    Worktree: objectstack-issue-21785
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface (at origin/main 18c2ddc1), per triage's direction 5987305576:

    • packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts: readDeclared reads the effective item, with the overlay included, so the boot sweep projects what the metadata door serves. The registry stays the one source, and the sending row stays its projection. Plus plugin-email tests.
    • The card's dogfood pin: edit through the metadata door, restart, and read the admin's wording back from the sending row. The data-door control (an edit there still survives a restart) stays. The pin goes in a NEW file under packages/qa/dogfood/test/, or extends the existing email-template-materialization.dogfood.test.ts; both are declared cross-lane on [PM seat] domain:cli — 🟢 marchtian · session_01B5CHJNXuuqzChM4w6hkTN4 #6024 in this act.
    • content/docs/** sentences this makes false, and a patch changeset.

    ⛔ No second "customized" marker for the metadata door (triage: it would be a second truth beside the overlay). ⛔ No change to the seed-not-clobber rules for admin-authored or data-door-customized rows. ⛔ No packages/spec edit.
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier: no path-derived mandate; default tier).
    Clause-②: no


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
    "issue": 21785,
    "status": "done",
    "branch": "claude/issue-21785-email-template-overlay-survives-boot",
    "pr": "#21818",
    "session": "session_011K3zqE8Pv1Evw5hc8tZCnN — the parent PM session (this run is a subagent); the harness Claude-Session line names the same id",
    "premise_still_valid": true,
    "summary": "The boot sweep now projects the EFFECTIVE email template: readDeclared reads protocol.getMetaItems (the layered list GET /meta/email_template serves) in tenancy.defaultOrgId()'s organization, so a metadata-door edit keeps the admin's wording in sys_email_template across a restart. A failed effective read projects nothing, and seed-not-clobber is unchanged. Measured on the real showcase: the defect lives only in the org-scoped shape (every Studio save on a deployment with a Default Organization; boot hydration keeps org-scoped overlays out of the registry), while an env-wide overlay already survived by registry insertion order. H2 as written was falsified: the door's effective reader answers the overlay only once an organization is named, so the organization comes from the platform's existing rule for org-less readers of org-overridable metadata (the anonymous form doors, precedent #21331): the Default Organization under single, none (env-wide) under any walled request.",
    "tests": [
    "Pre-fix (dogfood resolves plugin-email from dist/, built at base 18c2ddc, fix-marker count 0): new pin email-template-overlay-survives-boot.dogfood.test.ts → 1 failed | 2 passed; failure is the card symptom, row subject received "✅ Task done: …" instead of the admin wording after the restart.",
    "At 3dcc8cd (code-identical to head 94479dd, which adds only the changeset), under os-verify-lock: turbo build @objectstack/plugin-email exit 0; pnpm --filter @objectstack/plugin-email exec vitest run --maxWorkers=2 → Test Files 31 passed, Tests 514 passed; pnpm --filter @objectstack/plugin-email typecheck exit 0 (tsc --noEmit + check:test-typecheck OK, test layer compiles under tsconfig.test.json); dogfood vitest run --maxWorkers=2 test/email-template-overlay-survives-boot.dogfood.test.ts test/email-template-materialization.dogfood.test.ts → Test Files 2 passed, Tests 5 passed.",
    "Verbose run of src/bootstrap-declared-email-templates.test.ts: 21 passed, including the four #21785 pins (overlay projected in the default organization; env-wide read when defaultOrgId() is null; failed read projects nothing, never the package layer; seed-not-clobber over the effective read).",
    "At 94479dd: pnpm --filter @objectstack/dogfood typecheck exit 0; eslint --no-inline-config --format json over the 5 changed code files → 5 files, 0 errors, 0 warnings.",
    "Ablation A (the sweep reads the package layer again; scripts/ablation-replace.mjs --hold under a trap restore, absolute path): anchor 1 → 0, blob b0f455bd → cc6d6a3c; rebuild plugin-email; ablation-dist-preflight found the marker in dist/index.js and dist/index.mjs (exit 0). Unit: 4 failed | 17 passed (all four #21785 pins red). Dogfood: cold-boot pin red, received subject "✅ Task done: …". Restore: blob b0f455bd == HEAD blob, git diff HEAD empty; rebuild; preflight --absent exit 0.",
    "Ablation B (a failed effective read falls back to the registry; WRAP mode): failed-read pin red, received { seeded: 1 } expected { seeded: 0 }; 1 failed | 20 passed; restored blob == HEAD, git diff HEAD empty.",
    "NOT MEASURED: typecheck of plugin-email importers other than dogfood, reason: the public-surface change is additive only (an optional trailing parameter and one new exported type); no caller of bootstrapDeclaredEmailTemplates exists outside plugin-email (grep), and the one importing test package (dogfood) typechecks green.",
    "Lint, narrowed and proven: (1) population read from eslint.config.mjs: the 5 code files match its files globs and were linted; eslint --print-config on the .changeset file answers undefined (outside the population); (2) counts read from --format json: 5 files, 0 errors, 0 warnings; (3) invariance: the config never enables type-aware linting (zero parserOptions.project/projectService), its rule plugins are inline per-file AST rules, and its only file reads at config load are two baseline JSONs this diff does not touch, so the diff cannot move the verdict of any untouched file. Full pnpm lint is CI-owned."
    ],
    "gates": {
    "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at 94479dd vs merge base 18c2ddc: 6 paths, 68 commands; all 49 commands of the dispatch-time lead list are inside these 68.",
    "commands": [
    "exit 0 · node scripts/check-adr-0087-registration.mjs --base origin/main",
    "exit 0 · node scripts/check-adr-0087-registration.mjs --self-test",
    "exit 0 · node scripts/check-changeset-no-major.mjs --base origin/main",
    "exit 0 · node scripts/check-changeset-no-major.mjs --self-test",
    "exit 0 · node scripts/check-ci-filter-parity.mjs",
    "exit 0 · node scripts/check-closing-keyword-parity.mjs",
    "exit 0 · node scripts/check-closing-keyword-parity.mjs --self-test",
    "exit 0 · node scripts/check-comment-mask-adoption.mjs",
    "exit 0 · node scripts/check-comment-mask-adoption.mjs --self-test",
    "exit 0 · node scripts/check-comment-mask-corpus.mjs",
    "exit 0 · node scripts/check-dts-emitted.mjs --self-test",
    "exit 0 · node scripts/check-empty-changeset.mjs --base origin/main",
    "exit 0 · node scripts/check-empty-changeset.mjs --self-test",
    "exit 0 · node scripts/check-issue-citations.mjs",
    "exit 0 · node scripts/check-keyed-text-bounds.mjs",
    "exit 0 · node scripts/check-keyed-text-bounds.mjs --self-test",
    "exit 0 · node scripts/check-platform-object-tenancy-census.mjs",
    "exit 0 · node scripts/check-platform-object-tenancy-census.mjs --self-test",
    "exit 0 · node scripts/check-plugin-teardown-shape.mjs",
    "exit 0 · node scripts/check-plugin-teardown-shape.mjs --self-test",
    "exit 0 · node scripts/check-registry-log-declared.mjs",
    "exit 0 · node scripts/check-registry-log-declared.mjs --self-test",
    "exit 0 · node scripts/check-rest-log-spy-declared.mjs",
    "exit 0 · node scripts/check-rest-log-spy-declared.mjs --self-test",
    "exit 0 · node scripts/check-system-context-census.mjs",
    "exit 0 · node scripts/check-system-context-census.mjs --self-test",
    "exit 0 · node scripts/check-tenant-audit-census.mjs",
    "exit 0 · node scripts/check-tenant-audit-census.mjs --self-test",
    "exit 0 · node scripts/check-undeclared-dep-imports.mjs",
    "exit 0 · node scripts/check-undeclared-dep-imports.mjs --self-test",
    "exit 0 · node scripts/docs-audit/check-affected-docs.mjs",
    "exit 0 · node scripts/docs-audit/check-drift-comment.mjs",
    "exit 0 · node scripts/pm/release-rehearsal-clone.mjs --self-test",
    "exit 0 · node scripts/release-pending-publish.mjs --self-test",
    "exit 0 · pnpm --filter @objectstack/spec run check:duration-unit-keys",
    "exit 0 · pnpm --filter @objectstack/spec run check:empty-state",
    "exit 0 · pnpm --filter @objectstack/spec run check:liveness",
    "exit 0 · pnpm --filter @objectstack/spec run check:strictness-ledger",
    "exit 0 · pnpm --filter @objectstack/spec run check:variant-docs",
    "exit 0 · pnpm check:changeset-gate-self-tests",
    "exit 0 · pnpm check:cross-package-test-inputs",
    "exit 0 · pnpm check:doc-authoring",
    "exit 0 · pnpm check:driver-memory-census",
    "exit 0 · pnpm check:dts-closure",
    "exit 0 · pnpm check:dual-build-cjs-loads",
    "exit 0 · pnpm check:engine-double-contract",
    "exit 0 · pnpm check:gitlink-declared",
    "exit 0 · pnpm check:issue-citations",
    "exit 0 · pnpm check:lean-entry-closure",
    "exit 0 · pnpm check:logger-receiver-detach",
    "exit 0 · pnpm check:nul-bytes",
    "exit 0 · pnpm check:objectql-double-limit",
    "exit 0 · pnpm check:objectui-changeset",
    "exit 0 · pnpm check:org-identifier",
    "exit 0 · pnpm check:page-declaration-shape",
    "exit 0 · pnpm check:pm-changeset-deadline-census",
    "exit 0 · pnpm check:published-files",
    "exit 0 · pnpm check:query-options-erasure",
    "exit 0 · pnpm check:refd-timer-probe",
    "exit 0 · pnpm check:slot-lookup",
    "exit 0 · pnpm check:sourcemap-no-sources-content",
    "exit 0 · pnpm check:swallow-census-controls",
    "exit 0 · pnpm check:test-source-alias",
    "exit 0 · pnpm check:tier-file-adoption",
    "exit 0 · pnpm check:type-check-coverage",
    "exit 0 · pnpm check:type-check-debt",
    "exit 0 · pnpm check:watch-hint-literal",
    "exit 0 · pnpm check:where-matcher"
    ],
    "note": "check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: 8 packages outside this diff had no dist/); after turbo run build of those 8 (41 tasks, 41 cached) it exited 0 (106 require entry points across 66 packages load). The record carries that final measured code.",
    "ran_verdict": "dispatch-gates --ran exit 0: Run reconciliation — 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN."
    },
    "line_budget": "n/a",
    "deviations": [
    "H2 falsified and re-routed per the ruling intent: GET /meta serves the overlay only because the door passes the caller organization; getMetaItem/getMetaItems with no organization answer the package wording (measured at kernel:ready on boot 2). The sweep therefore also reads the tenancy service (defaultOrgId) beside the PM-suggested reader; no other route change.",
    "The dogfood pin boots with orgContext: true. It is plugin-auth production default (autoDefaultOrganization) and the only shape that reproduces; the harness default (org-less admin, env-wide overlay) passed before the fix, measured. A precondition case asserts the overlay is org-scoped.",
    "Measurement used a throwaway probe test file inside packages/qa/dogfood/test in my worktree. It was never committed, was removed before the ablations, and a copy is kept in the scratchpad.",
    "Unit/dogfood/typecheck ran at 3dcc8cd and gates at 94479dd; the only difference is the changeset file.",
    "Commit trailers use the AGENTS.md model-free pair (Claude-Session + Co-authored-by: Claude) rather than the harness reminder model-named form, per the dispatch contract.",
    "One pgrep -f lookup printed the asking shell pid; it was not used for any wait or kill (waits used the recorded os-verify-lock pids)."
    ],
    "files_changed": [
    "packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts (readDeclared effective read + EffectiveEmailTemplateSources + docblocks)",
    "packages/plugins/plugin-email/src/email-plugin.ts (resolve protocol + tenancy, pass to the sweep)",
    "packages/plugins/plugin-email/src/index.ts (export type EffectiveEmailTemplateSources)",
    "packages/plugins/plugin-email/src/bootstrap-declared-email-templates.test.ts (4 unit pins)",
    "packages/qa/dogfood/test/email-template-overlay-survives-boot.dogfood.test.ts (new; 3 cold boots on one file)",
    ".changeset/21785-email-template-overlay-survives-boot.md (new; @objectstack/plugin-email patch; Clause-②: no)",
    "totals: 6 files, +423 / -9 (dispatch-gates numstat); content/docs unchanged (no sentence made false)"
    ],
    "mcp_calls": "0",
    "api_writes": "3 — each one relay POST /repos/objectstack-ai/objectstack/dispatches (fleet-write, as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (#21818, draft, body read back 8598/8598 bytes identical); (2) label-write assign → POST /repos//issues/21818/assignees (os-steve, read back); (3) post-stamped os-dev-report comment → POST /repos//issues/21785/comments. git push x3 not counted (not REST).",
    "open_questions": [
    "[Redacted by the seat (domain:services#1, session_011K3zqE8Pv1Evw5hc8tZCnN) at 2026-10-05T05:05Z: an adjacent class outside this card, held by the seat for measurement. Detail is withheld from public surfaces.]"
    ],
    "out_of_scope_findings": [
    "[Redacted by the seat (domain:services#1, session_011K3zqE8Pv1Evw5hc8tZCnN) at 2026-10-05T05:05Z: an adjacent class outside this card, held by the seat for measurement. Detail is withheld from public surfaces.]",
    "carrier: PR #21818 Acceptance notes · noted, not filed — before this change the env-wide overlay shape survived only because the registry lists the hydrated overlay after the package entry, with a package-then-overlay double write per boot; now one write of the effective item.",
    "carrier: PR #21818 Acceptance notes · noted, not filed — packages/objectql/src/registry-i18n-bundle-key.test.ts comment says the materializer reads registry.listItems(email_template); still true in substance (it is getMetaItems base layer), left untouched (outside the file surface)."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: PR #21818 at 94479dd1 · patch round 1 · seat domain:services#1 · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-05T05:06Z

    Verdict on the dev report 5988422080 (status: done), checked against GitHub and the tree.

    Accepted:

    Patch round 1: one defect.

    • The PR widens plugin-email's published surface with zero consumers. The package entry gains type EffectiveEmailTemplateSources, and the index-exported bootstrapDeclaredEmailTemplates gains a parameter.
    • By the maintainer's ruling (batch [WIP] Add query enhancements and advanced validation features #35), a new exported symbol on an index takes at least minor. By the startup-focus axis, no consumer pulls it.
    • Criterion for the round: the package entry exports no new symbol and no exported function gains a parameter. The fix stays internal, the changeset stays patch, and the declaration stays Clause-②: no.

    Disclosure: an adjacent class outside this card is held by the seat for measurement. The passages describing it were redacted from the PR body and the report in this act.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
    "issue": 21785,
    "status": "done",
    "branch": "claude/issue-21785-email-template-overlay-survives-boot",
    "pr": "#21818",
    "head": "bae6303d",
    "session": "session_011K3zqE8Pv1Evw5hc8tZCnN — the parent PM session (this run is a subagent); the harness Claude-Session line names the same id",
    "premise_still_valid": true,
    "summary": "Patch round 1. plugin-email's published surface is back to base. The effective boot sweep is the module-internal bootstrapEffectiveEmailTemplates, which EmailServicePlugin calls with the protocol and tenancy services. The exported bootstrapDeclaredEmailTemplates keeps its four-parameter signature and delegates with no sources, so an external caller reads the registry as before: today's behaviour, without the effective read. There is one sweep body. Measured on dist/ at bae6303: no new symbol in index.d.ts or in either export list, and index.ts is identical to base. The changeset stays patch with Clause-②: no. Main is merged at 18c7dfd. Unit, typecheck, both dogfood files, both ablations (restores proven) and 68/68 gates (--ran 0 NOT-MEASURED) are green. The worktree is removed.",
    "tests": [
    "Surface proof on the built artifact at bae6303: src/index.ts vs base 18c2ddc → 0 diff lines; dist/index.d.ts mentions of EffectiveEmailTemplateSources → 0, of bootstrapEffectiveEmailTemplates → 0; ESM and CJS export lists → 93 keys each, internal function absent; published declaration bootstrapDeclaredEmailTemplates(engine: IDataEngine, metadataService: any, logger?: Logger, object?: string) equals the base source signature; exports map names only ".", tsup entry only src/index.ts, splitting off.",
    "At bae6303 under os-verify-lock: plugin-email build exit 0; pnpm --filter @objectstack/plugin-email exec vitest run --maxWorkers=2 → 31 files, 514 tests passed; verbose src/bootstrap-declared-email-templates.test.ts → 21 passed, including the four #21785 pins re-pointed at bootstrapEffectiveEmailTemplates; pnpm --filter @objectstack/plugin-email typecheck exit 0; dogfood vitest run --maxWorkers=2 test/email-template-overlay-survives-boot.dogfood.test.ts test/email-template-materialization.dogfood.test.ts → 2 files, 5 tests passed; pnpm --filter @objectstack/dogfood typecheck exit 0; eslint --no-inline-config --format json over the 5 changed code files → 5 files, 0 errors, 0 warnings.",
    "Ablation A at 7293048 (bae6303 differs only by moving a comment in the same file): mutation landed anchor 1 → 0, blob f73fb29e → d80f855d; rebuild; dist preflight found the marker in dist/index.js and dist/index.mjs; unit 4 failed | 17 passed (all four #21785 pins red); dogfood cold-boot pin red, received subject "✅ Task done: …"; restore blob f73fb29e == HEAD blob, git diff HEAD empty; rebuild; preflight --absent exit 0.",
    "Ablation B at 7293048 (failed effective read falls back to the registry): failed-read pin red, 1 failed | 20 passed; restored blob f73fb29e == HEAD, git diff HEAD empty.",
    "Lint narrowing evidence unchanged from round 0: population read from eslint.config.mjs (5 code files linted; the changeset is outside it), counts from --format json, and invariance (no type-aware linting, per-file AST rules, config-load reads only two untouched baseline JSONs)."
    ],
    "gates": {
    "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at bae6303 vs merge base 18c7dfd: 5 paths (index.ts no longer in the change set), 68 commands, identical to round 0.",
    "commands": [
    "exit 0 · node scripts/check-adr-0087-registration.mjs --base origin/main",
    "exit 0 · node scripts/check-adr-0087-registration.mjs --self-test",
    "exit 0 · node scripts/check-changeset-no-major.mjs --base origin/main",
    "exit 0 · node scripts/check-changeset-no-major.mjs --self-test",
    "exit 0 · node scripts/check-ci-filter-parity.mjs",
    "exit 0 · node scripts/check-closing-keyword-parity.mjs",
    "exit 0 · node scripts/check-closing-keyword-parity.mjs --self-test",
    "exit 0 · node scripts/check-comment-mask-adoption.mjs",
    "exit 0 · node scripts/check-comment-mask-adoption.mjs --self-test",
    "exit 0 · node scripts/check-comment-mask-corpus.mjs",
    "exit 0 · node scripts/check-dts-emitted.mjs --self-test",
    "exit 0 · node scripts/check-empty-changeset.mjs --base origin/main",
    "exit 0 · node scripts/check-empty-changeset.mjs --self-test",
    "exit 0 · node scripts/check-issue-citations.mjs",
    "exit 0 · node scripts/check-keyed-text-bounds.mjs",
    "exit 0 · node scripts/check-keyed-text-bounds.mjs --self-test",
    "exit 0 · node scripts/check-platform-object-tenancy-census.mjs",
    "exit 0 · node scripts/check-platform-object-tenancy-census.mjs --self-test",
    "exit 0 · node scripts/check-plugin-teardown-shape.mjs",
    "exit 0 · node scripts/check-plugin-teardown-shape.mjs --self-test",
    "exit 0 · node scripts/check-registry-log-declared.mjs",
    "exit 0 · node scripts/check-registry-log-declared.mjs --self-test",
    "exit 0 · node scripts/check-rest-log-spy-declared.mjs",
    "exit 0 · node scripts/check-rest-log-spy-declared.mjs --self-test",
    "exit 0 · node scripts/check-system-context-census.mjs",
    "exit 0 · node scripts/check-system-context-census.mjs --self-test",
    "exit 0 · node scripts/check-tenant-audit-census.mjs",
    "exit 0 · node scripts/check-tenant-audit-census.mjs --self-test",
    "exit 0 · node scripts/check-undeclared-dep-imports.mjs",
    "exit 0 · node scripts/check-undeclared-dep-imports.mjs --self-test",
    "exit 0 · node scripts/docs-audit/check-affected-docs.mjs",
    "exit 0 · node scripts/docs-audit/check-drift-comment.mjs",
    "exit 0 · node scripts/pm/release-rehearsal-clone.mjs --self-test",
    "exit 0 · node scripts/release-pending-publish.mjs --self-test",
    "exit 0 · pnpm --filter @objectstack/spec run check:duration-unit-keys",
    "exit 0 · pnpm --filter @objectstack/spec run check:empty-state",
    "exit 0 · pnpm --filter @objectstack/spec run check:liveness",
    "exit 0 · pnpm --filter @objectstack/spec run check:strictness-ledger",
    "exit 0 · pnpm --filter @objectstack/spec run check:variant-docs",
    "exit 0 · pnpm check:changeset-gate-self-tests",
    "exit 0 · pnpm check:cross-package-test-inputs",
    "exit 0 · pnpm check:doc-authoring",
    "exit 0 · pnpm check:driver-memory-census",
    "exit 0 · pnpm check:dts-closure",
    "exit 0 · pnpm check:dual-build-cjs-loads",
    "exit 0 · pnpm check:engine-double-contract",
    "exit 0 · pnpm check:gitlink-declared",
    "exit 0 · pnpm check:issue-citations",
    "exit 0 · pnpm check:lean-entry-closure",
    "exit 0 · pnpm check:logger-receiver-detach",
    "exit 0 · pnpm check:nul-bytes",
    "exit 0 · pnpm check:objectql-double-limit",
    "exit 0 · pnpm check:objectui-changeset",
    "exit 0 · pnpm check:org-identifier",
    "exit 0 · pnpm check:page-declaration-shape",
    "exit 0 · pnpm check:pm-changeset-deadline-census",
    "exit 0 · pnpm check:published-files",
    "exit 0 · pnpm check:query-options-erasure",
    "exit 0 · pnpm check:refd-timer-probe",
    "exit 0 · pnpm check:slot-lookup",
    "exit 0 · pnpm check:sourcemap-no-sources-content",
    "exit 0 · pnpm check:swallow-census-controls",
    "exit 0 · pnpm check:test-source-alias",
    "exit 0 · pnpm check:tier-file-adoption",
    "exit 0 · pnpm check:type-check-coverage",
    "exit 0 · pnpm check:type-check-debt",
    "exit 0 · pnpm check:watch-hint-literal",
    "exit 0 · pnpm check:where-matcher"
    ],
    "note": "The 8 out-of-closure packages check:dual-build-cjs-loads reads were built first (41 tasks, 35 cached), so every gate ran to a verdict on the first pass.",
    "ran_verdict": "dispatch-gates --ran exit 0: Run reconciliation — 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN."
    },
    "line_budget": "n/a",
    "deviations": [
    "Merged origin/main (18c7dfd) as 3191978; the auto-generated merge message named a private fetch ref, so it was reworded before the push (local, unpushed; no history rewrite of anything pushed).",
    "Two source comments that had gestured at the adjacent class held by the seat were reworded to state the read scope neutrally: one docblock sentence in bootstrap-declared-email-templates.ts, one test comment. The commits pushed in round 0 keep the old wording; no history was rewritten.",
    "The published docblock linked the internal function, and that link was carried into dist/index.d.ts. Found by the surface proof at 7293048 and moved into the function body at bae6303.",
    "Ablations ran at 7293048; the final head bae6303 only moves a comment in that file. Gates, unit, typecheck, dogfood and the surface proof ran at bae6303.",
    "The lock queue timed out twice (exit 99, holder #21771); both re-queues used the kept slot."
    ],
    "files_changed": [
    "packages/plugins/plugin-email/src/bootstrap-declared-email-templates.ts (published bootstrapDeclaredEmailTemplates restored to its 4-parameter signature, delegating; module-internal bootstrapEffectiveEmailTemplates carries the sources; EffectiveEmailTemplateSources marked module-internal)",
    "packages/plugins/plugin-email/src/email-plugin.ts (boot wiring calls bootstrapEffectiveEmailTemplates)",
    "packages/plugins/plugin-email/src/index.ts (back to byte-identical with base)",
    "packages/plugins/plugin-email/src/bootstrap-declared-email-templates.test.ts (four pins re-pointed at the internal function)",
    "packages/qa/dogfood/test/email-template-overlay-survives-boot.dogfood.test.ts (unchanged this round)",
    ".changeset/21785-email-template-overlay-survives-boot.md (patch, Clause-②: no; published-API-unchanged bullet replaces the fifth-argument bullet)",
    "totals vs merge base 18c7dfd: 5 files, +456 / -10"
    ],
    "mcp_calls": "0",
    "api_writes": "1 — one relay POST /repos/objectstack-ai/objectstack/dispatches (fleet-write, as objectstack-fleet[bot]) carrying post-stamped os-dev-report comment → POST /repos//issues/21785/comments. git push x3 this round (3191978..7293048, bae6303), not REST. No PR body PATCH.",
    "open_questions": [],
    "out_of_scope_findings": [
    "An adjacent class is held by the seat (detail withheld from public surfaces)."
    ],
    "private_measurement": "Delivered to the seat privately; not on any public surface."
    }


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 5, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:services#1 · session_011K3zqE8Pv1Evw5hc8tZCnN · 2026-10-05T07:06Z

    • PR fix(plugin-email): a metadata-door email template edit survives the next boot #21818 merged through the merge queue as 08adfeade8, which is an ancestor of origin/main (verified with git merge-base --is-ancestor after a fetch). Merged at 2026-10-05T06:51:52Z.
    • Fixes #21785 closed this card completed. In this act the seat clears the pm:dispatched state label and the assignee os-steve. The domain:services, area:api, priority:p2 and bug labels stay.
    • What shipped: an email template edited through the metadata door keeps the admin's wording across a restart. The boot sweep projects the effective (overlay-applied) template, read in the organization tenancy.defaultOrgId() names, the platform's existing rule for an org-less reader of org-overridable metadata. A failed effective read projects nothing, and seed-not-clobber is unchanged. plugin-email's published surface is unchanged (patch round 1).
    • Carried: an adjacent class outside this card is held by this seat for measurement and goes to the maintainer; its detail is withheld from public surfaces. The card's other notes are in PR fix(plugin-email): a metadata-door email template edit survives the next boot #21818's Acceptance notes.

    Generated by Claude Code

  8. added 3 commits that reference this issue on Oct 7, 2026
    08adfea
    56bf27a
    8c5aa50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions