Skip to content

feat(plugin-email)!: email templates resolve from the registry; the built-in seed, the boot sweep and the live projector retire (ADR-0131 D6/D10, C4 stage 2) - #22875

Merged
os-zhuang merged 22 commits into
mainfrom
claude/issue-15205-registry-loader
Oct 12, 2026
Merged

os-zhuang merged 22 commits into
mainfrom
claude/issue-15205-registry-loader

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #15205
Clause-②: yes (narrowing)

C4 stage 2, the registry loader half (ADR-0131 D6/D10, ruling C on §6 Q1, record 6020178017 on #22005). Claim 6109130514; dispatch from the domain:services seat 2. One cross-domain PR under triage's routing 6108741731 (the packages/spec ledger and describe text ride with the loader so check:liveness is never red on main).

What changed

  • @objectstack/plugin-email reads templates from the registry. createRegistryEmailTemplateLoader (in template-loader.ts) resolves email_template by (name, locale) at every send through the protocol's layered execution list (getMetaItemsForExecution — the environment's item over each code package's declaration), or the ObjectQL registry on a host with no metadata door. The locale ladder is the row loader's, unchanged. Within one slot any other package's item, or a package-less environment item, wins over the built-in. A failed read propagates (never answered from the registry's package wording); a malformed item refuses the send with the schema's message instead of falling through to the built-in copy.
  • The built-in auth templates are code-declared items of the email plugin's own package (com.objectstack.service.email), registered through its manifest at init. They now appear in Studio's email-template list; an app declaring the same (name, locale) wins; a Studio save overrides at environment level.
  • Retired: seedTemplates (the boot seed), bootstrap-declared-email-templates.ts whole (bootstrapDeclaredEmailTemplates, bootstrapEffectiveEmailTemplates, EffectiveEmailTemplateSources, upsertDeclaredEmailTemplate, deactivateDeclaredEmailTemplate, mapTemplateToRow, EMAIL_TEMPLATE_OBJECT), the live projector (wireTemplateMutationProjection, materializeTemplateMutation, readEffectiveTemplate, the metadata.subscribe watcher), createSysEmailTemplateLoader and its two types, and the seedTemplates / templates plugin options. The changeset names each with its FROM → TO line.
  • Kept: the sys_email_template object, its rows and the closed organization door (stage 1). No row is promoted or deleted: the customized rows are C7's (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211). TemplateLoader / EmailTemplateRow stay as the service's render seam.
  • packages/spec (describe-only, no schema shape change): the email_template liveness ledger is re-anchored by hand to the loader; the IEmailService contract docs, the notify node's template describe and refusal texts, and the email-template.zod.ts / notification.zod.ts prose name the registry, not rows. gen:docs regenerated two reference pages.
  • service-automation: the notify node's template help text. platform-objects: the sys_email_template header comment and provenance comment (no field help changed, so no translation churn).

Premise re-measured first (dispatch zone 2, items 1, 2, 4)

Scratch showcase measurement (never committed), orgContext: true, Default Organization bootstrapped, at d8c7d3864:

  • A Studio PUT /meta/email_template/showcase_task_done_email lands environment-scoped (sys_metadata.organization_id null).
  • Organization-free reads see it live (no restart) and after a cold boot: SchemaRegistry.getItem, protocol.getMetaItem, protocol.getMetaItems and getMetaItemsForExecution all serve the saved subject. metadataService.get / list answer nothing for the type.
  • Which read: the layered execution list. SchemaRegistry.listItems hands back the environment item beside the package entry it overrides, both wearing the package's _packageId / _provenance (measured), and getItem answers one bundle member per name. The list has already decided precedence, one item per (package, name, locale) slot.
  • Live without a restart: confirmed through the registry write-through; the projector had no job left and is gone (pinned: no projector, listener or subscription is armed; the PUT response carries no projectionApplied).

Byte identity

builtin-auth-render-identity.test.ts renders all 24 built-in templates (6 names x 4 locales) through EmailService.renderTemplate and pins subject (clear) plus HTML and text (SHA-256). At 924da8d8a it ran the seeded-row path (rows via mapTemplateToRow, the row loader) and the registry path side by side: identical for all 24. Since the row path's deletion it boots EmailServicePlugin over a real ObjectQL and holds the registry path to those digests.

Pins rewritten, not deleted (dispatch zone 2, item 7)

  • bootstrap-declared-email-templates.test.ts becomes template-loader.test.ts: every pin that asked whether the authored wording reaches the send is restated against the loader. Row-only mechanics (seed-not-clobber, provenance adoption, the bulk read) retired with the rows.
  • email-plugin.template-runtime-write.test.ts: a save sends on the very next send; no seam armed; no sys_email_template read or write, pre-existing rows byte-unchanged; the #16152 envelope pins moved to the loader path.
  • plugin-shutdown-detaches-template-bridge.test.ts: shutdown releases the door (the only engine binding left).
  • Dogfood: email-template-overlay-survives-boot (save environment-wide and live, survives a cold boot, a legacy customized row keeps its bytes and is not what sends, the closed data door), email-template-materialization (ADR-0054 proof keeps its @proof tag: no row, authored override beats the built-in, the built-in serves zh-CN, a Studio save over it sends without a restart), email-template-boot-sweep (real engine + SQL driver: zero statements reach the table on boot and send).
  • examples/app-showcase/test/email-template-locale.test.ts runs through the registry loader (Q2 answer A).
  • packages/spec/src/automation/io-node-config.test.ts: the describe pin asserted sys_email_template; it now asserts the email templates and refuses the row wording.

Ablations (each mutated, measured red, restored by blob equal to HEAD with an empty git diff HEAD)

Through scripts/ablation-replace.mjs, src-level at fcd3b610a: A1 a failed read falls back to the registry (2 red); A2 built-in precedence dropped (4 red); A3 malformed items skipped (3 red); A4 per-load read cached (6 red); A5 the plugin writes a row (1 red); A6 a projector armed (1 red); A7 the read names an organization (1 red). Dist leg: A5 rebuilt into dist/ (ablation-dist-preflight found the marker), the dogfood statement pin went 2 red, then restore, rebuild, --absent, 2 green. A8: the notify describe back to sys_email_template reddens the flipped spec pin. A9: the re-pointed swallow-census resolution control reddens with departure ONE removed (its first attempt was a no-op the tool refused; re-run in delete mode).

Evidence at 1cc2da623 (after merging origin/main 55382dc02)

  • Closure build (--filter=@objectstack/dogfood^... --filter=@objectstack/example-showcase...): 63/63, VERDICT command-exit 0.
  • plugin-email: 33 files / 515 tests pass; typecheck OK. spec: 648 files / 19,375 tests pass; typecheck OK. service-automation notify-node 16/16 and typecheck OK; platform-objects typecheck OK.
  • Dogfood email-template files 9/9; showcase locale test 6/6.
  • Gates: dispatch-gates --commands derived 136; --ran reconciles 136 run, 0 NOT-MEASURED. 135 exit 0. check:platform-checklist exits 1 with 8 findings in docs/qa/platform-checklist/areas/*.json naming metadata-protocol / service-storage symbols: the identical 8 on origin/main 69d421805 in a scratch worktree, so pre-existing and not from this branch.
  • Narrowed lint: eslint --no-inline-config --format json over the 33 changed source files: 33 files, 0 errors, 0 warnings, none ignored. The narrowing excludes nothing: eslint.config.mjs enables no type-aware linting, so this diff cannot move a verdict on an untouched file.

Needs a decision: email_template.category and .isSystem

Their only consumer was the row projection this PR retires (a Setup column, and the built-in seeder's stand-down). Measured: no reader in this repository, and at the .objectui-sha pin 0df67f23 objectui reads both only as editor inputs. The ledger grades both dead (honest measurement; label and description stay live on the Studio list's default columns). Consequence: packages/lint's runtime-gate.inert-type-writes.test.ts has 2 red cases, because a dead category now draws a liveness-dead-property advisory, and category is schema-defaulted ('custom'), so every template carries it and the advisory cannot be cleared by the author. Not resolved here: retiring the keys is a schema shape change this dispatch fences out, and packages/lint is outside the file surface. Options and the four-axis analysis are in the report comment on #15205.

Acceptance notes

  • Gate-forced edits outside the named surface, each the minimal answer to a gate naming retired code: scripts/engine-double-contract.baseline.json and scripts/objectql-double-limit.baseline.json (shrink-only rows for files now gone or clean), scripts/isystem-census.mjs (a NON_ELEVATION_READS row for the deleted module), scripts/measure-durability-swallow-family.mjs (a resolution control re-pointed by measurement: 21 departure-ONE sites remain, none write-shaped), the regenerated censuses (content/docs/permissions/system-context.mdx, tenant-audit-census.mdx with its hand-written figures, docs/audits/2026-08-tenant-audit-write-call-sites.counts.md), and packages/spec/liveness/README.md plus the generated state-counts/email_template.md.
  • Cross-lane docs edit, its own commit 94ab10722 (droppable): content/docs/automation/email-templates.mdx described the materialization and seed-not-clobber this PR retires.
  • Transitional, as ruled: a sys_email_template row an organization customized is no longer what sends until C7 promotes it. The changeset says so and names the re-save in Studio as the interim fix.
  • Pre-existing defect observed (not this card's): sys_metadata holds one overlay row per (type, name), so a Studio save of a second locale member of one email_template name replaces the first member's overlay at rest; live reads still show both until a restart, then the first reverts to package wording. Measured on the showcase; the row path showed the same revert at boot. Reported for filing.
  • Comment-only references to retired names left outside the surface: packages/objectql/src/registry.ts, registry-i18n-bundle-key.test.ts, packages/spec/src/stack-email-template-locale-floor.test.ts, packages/spec/scripts/liveness/proof-registry.mts (class prose), and the dogfood fixture fixtures/email-template-materialization-fixture.ts (header and manifest description).
  • Changed lines: 4,490 (additions 1,517 + deletions 2,973, generated included). Over 3,000, so this lands the Tier H way. No governed surface in the file list.

Patch round: the seat's answer B to Q1

The seat answered Q1 with B (6111829211): email_template.category and .isSystem land graded dead, and #22876 retires them in the next enforce-or-remove batch.

  • packages/lint/src/runtime-gate.inert-type-writes.test.ts: the group-C case is split. mapping keeps its assertions and title. email_template now pins exactly one advisory, rule liveness-dead-property on property category. The shipped-corpus control pins the same single advisory; its isSystem: false draws nothing.
  • packages/lint/src/lint-liveness-properties.test.ts: a template that sets neither key still warns about nothing. category, and isSystem: true, each draw one dead-property advisory. The comments cite [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876.
  • The tests assert the advisory's rule id and property path, not its wording. The lint rule, the ledger grading and the schema are untouched.
  • Ablations L1 to L4 each went red (1 failed of 142) and were restored by blob.
  • Merged origin/main 0253192f7 (81ec7792b) and regenerated the censuses (33641fe78).
  • At 33641fe78: lint 6,461/6,461 and typecheck OK; plugin-email 515/515; dogfood 9/9.
  • Gates: 136 derived, all run, 0 NOT-MEASURED. 135 exit 0. check:platform-checklist fails with the same 8 findings already present on origin/main 69d421805.
  • 4,575 changed lines against 0253192f7.

Patch round 3: the Clause-② line carries the narrowing arm

The seat amended its claim (6112650531): this PR retires published exports and plugin options of @objectstack/plugin-email, a BREAKING narrowing of the package's public face that widens nothing, so the declaration reads Clause-②: no (narrowing). The bare no was the seat's own error in the claim.

  • One commit, 4aa656e7a on top of 33641fe78, changes one line of .changeset/15205-email-templates-resolve-registry.md: Clause-②: no → Clause-②: no (narrowing). The BREAKING banner, the minor grade for @objectstack/plugin-email, the two patch rows and the ADR-0087 marker not-required (no-migration-prescription) are unchanged. This body's second line carries the same arm.
  • check-adr-0087-registration --base origin/main exits 0 and reads the changeset as [BREAKING+bang+clause-②-narrowing], accepting the unchanged disposition. check-changeset-no-major exits 0 in its local form and in CI's form (--base the merge base, --event a pull_request payload with this line 2): "the declared narrowing is accounted for: @objectstack/plugin-email: minor". check-empty-changeset exits 0.
  • dispatch-gates --commands for the changeset path: 20 derived, all 20 run at 4aa656e7a, all exit 0; --ran 0 NOT-MEASURED, 0 UNRUN. No package build or test was owed.
  • Changed lines: unchanged at 4,575.

Patch round 4: the at-tier review FAIL 6113872462

The seat amended its claim a second time (6113893432). The PR widens @objectstack/plugin-email's public face (createRegistryEmailTemplateLoader, EmailTemplateRegistrySources) and also narrows it (the retirements), so the declaration is Clause-②: yes (narrowing). That is on this body's second line, in the changeset and on the claim. The seat's earlier no and no (narrowing) were both its own error.

  • 2c11a7675 (changeset):
    • The Clause-② line now reads yes (narrowing). The minor grade and the BREAKING banner stay.
    • The ADR-0087 marker keeps not-required (no-migration-prescription). Its why gains one sentence: the FROM → TO block above is a code change the compiler carries, not a metadata migration for objectstack migrate meta. ADR-0087's vocabulary has no category for that shape exactly, so the maintainer is asked to rule the reading.
    • check-adr-0087-registration --base origin/main exits 0 and prints that sentence in its reason.
  • 0f1906217 (packages/spec/liveness/email_template.json): the cross-repo evidence is re-measured at the tree's .objectui-sha pin 4997995022f8 and restated with file:line. That covers the label and description evidence and the category and isSystem dead notes.
    • ResourceListPage.tsx#defaultColumns (:835, rendering label :838 and description :839), and its fallback at :421.
    • default-schemas.ts: email_template is registered at :336 with no listColumns. category (:310) and isSystem (:324) are editor inputs only.
    • No reading changed from 0df67f23: none of the cited files is among the 80 files the 7 commits between the two pins touch. No grade moves.
  • Gates at 0f1906217, for the two changed paths: 60 derived, 59 run, 1 NOT-MEASURED, 0 UNRUN; 57 exit 0, check:liveness among them.
    • check:platform-checklist exits 1 with the same 8 findings as on origin/main 45e206e49. That is pre-existing; see the note below.
    • check:dual-build-cjs-loads is NOT MEASURED. It needs every package's dist/, and this round changes no build input, so CI runs it.
    • In CI mode, check-changeset-no-major with this line 2 reports "the declared narrowing is accounted for: @objectstack/plugin-email: minor".
  • Changed lines: unchanged at 4,575.

Seat's acceptance notes after the review

  • The destroy() drain-hook finding: EmailServicePlugin.destroy() leaves the sys_email outbox drain hook bound on the engine after kernel shutdown. Carrier: none. It is noted here and not filed. Its only reach is an engine that outlives its kernel, measured on a test fake, which is below the filing gate's measured-reach bar. It is pre-existing and not this PR's. A semantic search for an open card on the class found none.
  • check:platform-checklist red on main: the 8 findings are the known red that triage closed not_planned twice, check:platform-checklist is red on main #22594 and check:platform-checklist is red on main #22758 (6093524974 sets the direction). Neither card's re-entry line is met. The gate is kept out of per-PR CI by standing decision (.github/workflows/checklist-status.yml). This seat files nothing.
  • For the maintainer, who approves this Tier H landing in any case: ADR-0087's disposition vocabulary has no category that verifiably fits a compiler-carried removal of runtime exports in a diff that also touches packages/spec. This changeset and stage 1's (15205-email-template-org-door-closed.md) both hold not-required (no-migration-prescription) on that reading. The open choice: rule that reading into the ADR's addendum, or add a seventh category. The at-tier review 6113872462 raised it, and it does not block this PR's substance.

Merge round: origin/main 3fdb92db0, through os-regen-merge.sh

The PR was conflicted against main (mergeable_state: dirty), so no pull_request CI ran on 0f1906217. Two commits fix that; the head is now 78c3eed63 and the merge base 3fdb92db0.

  • be55432f6 merges origin/main 3fdb92db0 through scripts/pm/os-regen-merge.sh. The one conflict was the census figures table in content/docs/permissions/system-context.mdx, a MIXED merge=os-regen path, with no prose in the hunk. It was resolved to main's figures, and the PR's row-58 prose stayed.
  • 78c3eed63 re-runs the repo's own generators only: gen:system-context-census and tenant-audit-census.mjs --write. Spec check:generated is green, check:docs included.
  • Gates at 78c3eed63: 136 derived, 136 run, 0 NOT-MEASURED, 0 UNRUN; 135 exit 0. check:platform-checklist's 8 findings are identical at the merge base 3fdb92db0.
  • Pure regeneration (Regen-provenance 6114859685, re-run by the seat on the committed trees): the net diff covers the same 49 paths before and after. Exactly 3 paths' changes differ, all generator output: one census figure (main's base moved, the PR's −1 unchanged) and two measured-at stamps. The at-tier PASS 6114282872 carries to this head.
  • Changed lines: unchanged at 4,575.

Generated by Claude Code

claude added 14 commits October 11, 2026 13:18
…ntical to the seeded rows

createRegistryEmailTemplateLoader resolves email_template metadata by
(name, locale) through the protocol's layered execution list (environment
over code), falling back to the ObjectQL registry on a host with no
metadata door. The locale rules are the row loader's, unchanged; within one
slot any other package's item wins over the built-in package's.

builtin-auth-render-identity.test.ts renders all 24 built-in auth
templates through EmailService.renderTemplate over both the seeded-row path
and the registry path and pins both against digests captured from the row
path: identical.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…t sweep and the live projector

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…y read, not the retired rows

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…he registry loader; describe text names the registry, not sys_email_template rows

The ledger is hand-written measurement, re-anchored by hand: every live key
now cites template-loader.ts#toRenderInput / #createRegistryEmailTemplateLoader
and the service's reads. category and isSystem re-grade live -> dead (their
only consumer was the retired row projection); label and description stay live
on the Studio metadata list (objectui pin 0df67f23). No schema shape change.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…ow, not the retired rows

Cross-lane, kept in its own commit: both pages state the row path this PR
retires, and the role file makes text this change falsifies a must-fix.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…cribe text (gen:docs)

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…mail minor, BREAKING; spec, service-automation patch)

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…tes, not the retired rows

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…h; regenerate the two censuses

- engine-double-contract: both rewritten plugin-email doubles keep their pinned
  update() and route it through assertEngineUpdateDispatch; the baseline row
  for the deleted bootstrap test is removed (shrink).
- objectql-double-limit baseline: three rows for files now clean or gone
  removed (shrink).
- isystem-census: the NON_ELEVATION_READS row for the deleted
  bootstrap-declared-email-templates.ts removed; gen:system-context-census
  rewrote the declared counts.
- measure-durability-swallow-family: the departure-ONE resolution control
  that pinned EmailServicePlugin.upsertTemplate is re-pointed, by
  measurement, to a site that still exists.
- tenant-audit-census --write, plus the page's hand-written figures
  (233 -> 228, 158 -> 156, 75 -> 72, 126 -> 121).

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…gistry-loader

# Conflicts:
#	content/docs/permissions/system-context.mdx
#	content/docs/permissions/tenant-audit-census.mdx
#	docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
… on the merged tree

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…and isSystem now draw

ADR-0131 D6/D10 retired the sys_email_template row projection, the only reader
of both keys, and the ledger grades them dead. The group-C email_template case
and the shipped-corpus control now assert exactly that advisory (rule id and
property path), and the liveness case pins category and a truthy isSystem.
The mapping row is unchanged. The retirement batch removes the advisories.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
…gistry-loader

# Conflicts:
#	content/docs/permissions/system-context.mdx
#	content/docs/permissions/tenant-audit-census.mdx
#	docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
… on the merged tree

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation protocol:system tests tooling labels Oct 11, 2026
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/platform-objects, @objectstack/plugin-email, @objectstack/service-automation, @objectstack/spec, touching 68 documentable anchor(s). ⚠️ 6 changed file(s) yielded no anchor (packages/plugins/plugin-email/src/templates/auth-templates.ts, packages/spec/liveness/README.md, packages/spec/liveness/email_template.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 3f694715ee43565502bd70d6b3a64b28033531b4.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 6 changed file(s) yielded no anchor (packages/plugins/plugin-email/src/templates/auth-templates.ts, packages/spec/liveness/README.md, packages/spec/liveness/email_template.json, …) — pages documenting those are invisible to this run
  • 13 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 140 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 3f694715ee43565502bd70d6b3a64b28033531b4 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c3748bd364a84916c236129fc4fbd8a3b9c52303 — the merge of head 78c3eed6389de37493b25586bfb6c48f02c63188 into base 3f694715ee43565502bd70d6b3a64b28033531b4, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c3748bd364a84916c236129fc4fbd8a3b9c52303 && git checkout c3748bd364a84916c236129fc4fbd8a3b9c52303
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3f694715ee43565502bd70d6b3a64b28033531b4 78c3eed6389de37493b25586bfb6c48f02c63188 && git checkout -B drift-repro 3f694715ee43565502bd70d6b3a64b28033531b4 && git merge --no-ff 78c3eed6389de37493b25586bfb6c48f02c63188

node scripts/docs-audit/affected-docs.mjs --json 3f694715ee43565502bd70d6b3a64b28033531b4

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3f694715ee43565502bd70d6b3a64b28033531b4 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

The PR retires published exports and plugin options of @objectstack/plugin-email,
a breaking narrowing of the package's public face; the bare 'no' named no arm.
Only the declaration line changes: the BREAKING banner, the minor grade and the
ADR-0087 disposition stay.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4aa656e7a2f67d213e01a885960d281937483c03
Local-runs: none

Rendered by an isolated reviewer subagent of the seat in Reviewed-by, which adopts it.

Inputs, read at 2026-10-11T21:18Z: card #15205 (body and all 21 comments; the seat's ACCEPT 6112901622 and its Q1 answer 6111829211 read as data), ruling record 6020178017 on #22005, triage routing 6108741731, PR #22875 (body, 49-file list, net diff against main at the merge base 0253192f7), and the head's check-runs: 46 runs, 39 success, 7 skipped, 0 failed; latest run per name 31 success and 4 skipped (Auto Label, Check PR Size, Console Pin Gate, Packed-tarball smoke), none in progress. Every gate verdict is green.

① Derived judgments

  1. @objectstack/plugin-email public face narrows — right. src/index.ts drops createSysEmailTemplateLoader, TemplateLoaderEngine, CreateTemplateLoaderOptions, bootstrapDeclaredEmailTemplates, upsertDeclaredEmailTemplate, deactivateDeclaredEmailTemplate, mapTemplateToRow, EMAIL_TEMPLATE_OBJECT, BootstrapDeclaredEmailTemplatesResult; EmailServicePluginOptions loses seedTemplates and templates. Each is named in the changeset with its FROM → TO line. git grep at the head finds no remaining consumer outside tests, CHANGELOGs and comments.
  2. @objectstack/plugin-email public face also WIDENS — right as code, wrong as declared. src/index.ts adds createRegistryEmailTemplateLoader and the type EmailTemplateRegistrySources, both reachable from the package's single exports entry. Under the claim rule (公开面 = 包构建后的入口声明所达;所达 = 再导出名,加经其 props、参数与返回类型可达的每个类型) two new reachable declarations enlarge the public face. The claim's reason for no — "no published type widens" — is false: EmailTemplateRegistrySources is a new published type. The board's recent declarations read a new published member the same way (feat(spec): declare IWebhookService.handleRedeliver, the optional transport-neutral webhook redeliver member #22767 and feat(spec): declare ITriggerApiService.handleInboundHook, the optional transport-neutral inbound-hook member #22779 yes (widening); feat(plugin-security,plugin-auth,verify,platform-objects)!: a permission-set grant names its set by name — no writer fills permission_set_id (#15204 stage 6b-2-pre) #22863 yes (narrowing) for a replace). The accurate line is yes (narrowing); the level it owes (at least minor) is already met. See ②.
  3. BUILTIN_EMAIL_TEMPLATES_PACKAGE_ID — right. Exported by template-loader.ts but not by the entry, and the exports map has . only, so it is not public.
  4. Resolution behaviour against ruling C, D6 and D10 — right. createRegistryEmailTemplateLoader reads email_template at every load from protocol.getMetaItemsForExecution (the environment's item over each package's declaration; no default page limit was found on that read) or from the ObjectQL registry when no protocol is registered; the read names no organization (option A of §6 Q1 stays rejected); within one (name, locale) slot any item not under com.objectstack.service.email wins over the built-in; a failed read propagates; a malformed item refuses the send with the schema's message. The locale ladder is unchanged. The built-ins are code-declared through the plugin manifest's emailTemplates key, which registerApp consumes. All 24 built-ins are pinned byte-identical to the seeded-row render (subject in clear, html and text by SHA-256).
  5. Retirement list against the ruling — right. seedTemplates, the boot sweep (bootstrapEffectiveEmailTemplates / EffectiveEmailTemplateSources, the two names the ruling added), the live projector and the row loader are gone. The plugin neither reads nor writes sys_email_template; the stage-1 organization door stays bound and closed; no row is promoted or deleted (C7, feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211); the table is not retired (D13). The ADR dated note is not in this diff and already sits on main (§6 Q1, "Ruled 2026-10-06: C"), as stage 1 carried it.
  6. Wire shape — right. PUT /api/v1/meta/email_template/:name no longer carries projectionApplied, an optional receipt key; the changeset says so.
  7. packages/spec published face — right, with one evidence defect. Describe and TSDoc text, plus two refusal messages, in automation/io-node-config.zod.ts, contracts/email-service.ts, system/email-template.zod.ts and system/notification.zod.ts; no shape change. The liveness ledger re-anchors every live row to template-loader.ts#toRenderInput / #createRegistryEmailTemplateLoader and the service's reads, and re-grades category and isSystem dead (their only reader was the retired row projection — an honest measurement). Defect: the ledger's cross-repo evidence for label and description, and the dead notes for category and isSystem, are stated "at the .objectui-sha pin 0df67f23", but .objectui-sha at both the merge base and this head is 4997995022f8. A hand-written measurement cited at a superseded pin is unverifiable against this tree.
  8. @objectstack/service-automation — right (the notify node's template help text names the email templates). @objectstack/platform-objects — right (header and provenance comments only; no field help, no published text).
  9. Lint behaviour — right, carried. Through the ledger, @objectstack/lint now draws one liveness-dead-property advisory for category and for a truthy isSystem; the two packages/lint cases pin rule id and property path, not wording. The built-ins themselves (category: 'auth', isSystem: true) trip it; [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876 retires both keys.
  10. Gate-forced edits — right. Shrink-only baseline rows, the isystem-census row for the deleted module, the re-pointed swallow-family control, regenerated censuses and reference pages; the showcase test and the three dogfood files are re-pinned against the loader, not deleted.

② Semver level

  • @objectstack/plugin-email: minor with the BREAKING banner — right: main is in pre mode (next) and check-changeset-no-major forbids major in the launch window. @objectstack/spec: patch and @objectstack/service-automation: patch — right (text and a measurement ledger; no shape). No row for platform-objects, lint, dogfood or the showcase — right (comments and tests only).
  • Clause-②: no (narrowing) — the arm is right, the value is wrong. The diff narrows AND widens the published face of @objectstack/plugin-email (① 1 and 2). The one legal spelling for that is yes (narrowing) — a diff that widens one surface and narrows another, both facts read. All three carriers read no: PR body line 2, .changeset/15205-email-templates-resolve-registry.md, and the claim 6109130514 as amended by 6112650531. Both changeset gates stay green either way; what is wrong is the declaration, which is one of the three faces this review exists to check.
  • ADR-0087 marker not-required (no-migration-prescription) — substance right, category wrong by the ADR's letter; escalated, not charged to the dev. Nothing moves for objectstack migrate meta (no spec key, stored shape or row), so no ledger entry is the right answer. But the body carries a FROM → TO block (six lines under "What moves for consumers"), and ADR-0087's 2026-08-13 addendum reads this category as refused by a body that carries a migration prescription; the gate admits it only because the lines carry no arrow, which its detector requires. No admissible category exists for this shape: runtime-interface-only names interfaces or types that still exist at HEAD and inherits the same refusal; type-surface-only fails predicate 2 because the diff touches packages/spec. Stage 1's changeset (15205-email-template-org-door-closed.md, maintainer-approved) holds the same category for the same class. The gap in the vocabulary, not this PR, is the finding — see ③.

③ Boundary flags

What clears this FAIL — one commit plus two text edits: (1) Clause-②: yes (narrowing) on the changeset line, on PR body line 2 and in a claim amendment; (2) the liveness ledger's cross-repo evidence re-measured, or restated, at the tree's pin 4997995022f8; (3) a carrier for the drain-hook finding; (4) the ADR-0087 marker's why naming the FROM → TO block and the vocabulary gap it sits in, for the maintainer's ruling.

Implemented-by: claude/issue-15205-registry-loader
Reviewed-by: session_013LbZ9MhPp1iriZEtgJqioA

VERDICT: FAIL


Generated by Claude Code

The PR narrows @objectstack/plugin-email's public face (nine exports and two
plugin options retire) and also widens it: createRegistryEmailTemplateLoader and
the type EmailTemplateRegistrySources are new names reachable from the package's
single exports entry. A diff that widens one surface and narrows another is
declared 'yes (narrowing)'. The minor grade and the BREAKING banner stay.

The ADR-0087 marker keeps not-required (no-migration-prescription) and its why
gains one sentence: the body's FROM -> TO block is a compiler-carried code change,
not a metadata migration, and the disposition vocabulary has no category that
fits this shape exactly, so the maintainer is asked to rule the reading.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
… 4997995022f8

The cross-repo evidence for label and description, and the dead notes for
category and isSystem, were measured at objectui 0df67f23, which is not the
.objectui-sha pin of this tree (4997995022f8 at the merge base and the head).
Each cited read was re-measured at 4997995022f8e65946f6362b7434685539cc72b7 and
restated there with the file:line read: ResourceListPage.tsx#defaultColumns
(838 label, 839 description; the fallback at 421; the cell read at 687),
default-schemas.ts#registerDefaultMetadataSchemas:336 (email_template registers
no listColumns), and default-schemas.ts:310 / :324 (category and isSystem as
editor inputs only). No reading changed between the two pins; no grade moves.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0f1906217a83ce3381bc90bab6db5dee15d5ebf5
Local-runs: none

Rendered by an isolated reviewer subagent of the seat in Reviewed-by, which adopts it.

Second record on this PR. It supersedes the FAIL 6113872462 on 4aa656e7a; the four edits that record named are re-judged below together with everything else on this head. Inputs, read at 2026-10-11T21:59Z: card #15205 (body and all 24 comments — the seat's ACCEPT 6112901622, its re-review 6114212452, its Q1 answer 6111829211 and every dev report read as data), ruling record 6020178017 on #22005, triage routing 6108741731, the claim 6109130514 as amended by 6113893432, PR #22875 (the body as it now stands, the 49-file list, and the net diff against main at the merge base 0253192f7, unchanged at 49 files, +1,581 / −2,994; the delta since 4aa656e7a is two commits, 2c11a7675 and 0f1906217, touching only the changeset and packages/spec/liveness/email_template.json, +8 / −8), and the head's check-runs: zero check-runs are attached to this head at 2026-10-11T21:59Z (the API lists all five check-suites as queued with no runs, roughly half an hour after the commit landed at 21:30Z), so no gate has concluded on this head yet and no gate verdict is cited here; a run not yet started is not a failure. For scale, the previous head 4aa656e7a concluded 39 success, 7 skipped, 0 failed, and the delta to this head is two text files (a changeset and a ledger JSON) that no build compiles in. The seat's own pre-landing check reads this head's runs when they conclude; this record does not claim them green.

① Derived judgments

  1. @objectstack/plugin-email public face narrows — right. src/index.ts drops createSysEmailTemplateLoader, TemplateLoaderEngine, CreateTemplateLoaderOptions, bootstrapDeclaredEmailTemplates, upsertDeclaredEmailTemplate, deactivateDeclaredEmailTemplate, mapTemplateToRow, EMAIL_TEMPLATE_OBJECT, BootstrapDeclaredEmailTemplatesResult; EmailServicePluginOptions loses seedTemplates and templates. Each is named in the changeset with its FROM → TO line; git grep at the head finds no remaining consumer outside tests, CHANGELOGs and comments.
  2. @objectstack/plugin-email public face also widens — right as code and, on this head, right as declared. src/index.ts adds createRegistryEmailTemplateLoader and the type EmailTemplateRegistrySources, both reachable from the package's single exports entry; createRegistryEmailTemplateLoader reaches the existing TemplateLoader. Two new reachable declarations enlarge the public face (公开面 = 包构建后的入口声明所达), and the declaration now says so: yes (narrowing).
  3. BUILTIN_EMAIL_TEMPLATES_PACKAGE_ID — right. Exported by template-loader.ts, not by the entry; the exports map has . only, so it is not public.
  4. Resolution behaviour against ruling C, D6 and D10 — right. The loader reads email_template at every load from protocol.getMetaItemsForExecution (the environment's item over each package's declaration; no default page limit was found on that read) or from the ObjectQL registry when no protocol is registered; the read names no organization (option A of §6 Q1 stays rejected); within one (name, locale) slot any item not under com.objectstack.service.email wins over the built-in; a failed read propagates; a malformed item refuses the send with the schema's message; the locale ladder is unchanged. The built-ins are code-declared through the plugin manifest's emailTemplates key, which registerApp consumes, and all 24 are pinned byte-identical to the seeded-row render (subject in clear, html and text by SHA-256).
  5. Retirement list against the ruling — right. seedTemplates, the boot sweep (bootstrapEffectiveEmailTemplates / EffectiveEmailTemplateSources, the two names the ruling added), the live projector and the row loader are gone. The plugin neither reads nor writes sys_email_template; the stage-1 organization door stays bound and closed; no row is promoted or deleted (C7, feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211); the table is not retired (D13). The ADR dated note is not in this diff and already sits on main (§6 Q1, "Ruled 2026-10-06: C"), as stage 1 carried it.
  6. Wire shape — right. PUT /api/v1/meta/email_template/:name no longer carries projectionApplied, an optional receipt key; the changeset says so.
  7. packages/spec published face — right; the evidence defect is cleared. Describe and TSDoc text plus two refusal messages in automation/io-node-config.zod.ts, contracts/email-service.ts, system/email-template.zod.ts and system/notification.zod.ts; no shape change. The liveness ledger re-anchors every live row to template-loader.ts#toRenderInput / #createRegistryEmailTemplateLoader and the service's reads, re-grades category and isSystem dead (their only reader was the retired row projection), and on this head its cross-repo evidence for label and description and its dead notes for category and isSystem are stated at the tree's .objectui-sha pin 4997995022f8 (the pin at both the merge base and the head), each with a file and line in ResourceListPage.tsx and default-schemas.ts. This reviewer has no objectui checkout and did not open one (outside the inputs); the dev's method is stated on the card — 0df67f23 is an ancestor of the pin, and the seven commits between them touch none of the cited files — and the grades are unchanged (19 live, 2 dead, 21 classified). The Spec property liveness check-run is the gate verdict on the ledger's shape.
  8. @objectstack/service-automation — right (the notify node's template help text names the email templates). @objectstack/platform-objects — right (header and provenance comments only; no field help, no published text).
  9. Lint behaviour — right, carried. Through the ledger, @objectstack/lint draws one liveness-dead-property advisory for category and for a truthy isSystem; the two packages/lint cases pin rule id and property path, not wording; the built-ins (category: 'auth', isSystem: true) trip it, and [finding] email_template.category and email_template.isSystem have no consumer once templates resolve from the registry: retire both in the next enforce-or-remove batch #22876 retires both keys.
  10. Gate-forced edits — right. Shrink-only baseline rows, the isystem-census row for the deleted module, the re-pointed swallow-family control, regenerated censuses and reference pages; the showcase test and the three dogfood files are re-pinned against the loader, not deleted. No governed path is in the file list.

② Semver level

  • @objectstack/plugin-email: minor with the BREAKING banner — right: main is in pre mode (next) and check-changeset-no-major forbids major in the launch window. @objectstack/spec: patch and @objectstack/service-automation: patch — right (text and a measurement ledger; no shape). No row for platform-objects, lint, dogfood or the showcase — right (comments and tests only).
  • Clause-②: yes (narrowing) — right, on all three carriers: PR body line 2, the changeset at 2c11a7675, and the claim amendment 6113893432 (line-start key, the one spelling clause2-line.mjs reserves for a diff that widens one surface and narrows another). What yes owes is met: at least minor on a package whose published source moves (plugin-email); the spec-lane routing triage already made into this one cross-domain PR (6108741731); and the at-tier review, which is this record.
  • ADR-0087 marker not-required (no-migration-prescription) — substance right; the category still has no admissible alternative; the why is now honest. Nothing moves for objectstack migrate meta, so no ledger entry is the right answer. The body still carries a six-line FROM → TO block, and the appended sentence now says so, names it as a code change the compiler carries, names the vocabulary gap, and asks the maintainer to rule. Read against the gate's own detector, the new sentence changes nothing mechanically: its FROM → TO is two bare words, not operands, and the list lines carry no arrow, so the gate's green on this head rests on the same miss as before — declared in writing this time, which is what the FAIL asked for. runtime-interface-only and type-surface-only stay closed (live interfaces at HEAD, and predicate 2 fails on a diff touching packages/spec). The ruling stays with the maintainer; see ③.

③ Boundary flags

Implemented-by: claude/issue-15205-registry-loader
Reviewed-by: session_013LbZ9MhPp1iriZEtgJqioA

VERDICT: PASS


Generated by Claude Code

…gistry-loader

origin/main 3fdb92d merged through scripts/pm/os-regen-merge.sh. The one
conflict, the census figures table of content/docs/permissions/system-context.mdx
(a MIXED os-regen path), is resolved to main's side of the figures; the PR's row-58
prose stays. The figures are re-derived by step 4's regeneration.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
… on the merged tree

Step 4 of scripts/pm/os-regen-merge.sh after merging origin/main 3fdb92d, from
the repo's own generators only: pnpm gen:system-context-census and
node scripts/tenant-audit-census.mjs --write. The system-context figures read the
merged tree (129 property reads, 5 of them unrelated metadata fields: main's 130
and 6, less the retired row projection's isSystem read); the tenant-audit page and
counts restamp their measured-at commit. Spec check:generated is green.

Claude-Session: https://claude.ai/code/session_013LbZ9MhPp1iriZEtgJqioA
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6114282872 · 0f1906217a → 78c3eed638 · per-path net-diff comparison outside generator output → (empty)

domain:services seat 2 (#21118) · session_013LbZ9MhPp1iriZEtgJqioA · 2026-10-11T22:59Z. A pointer for re-running the test, ⛔ not the evidence.

  • The move: merge be55432f6 of origin/main 3fdb92db0 through scripts/pm/os-regen-merge.sh, then 78c3eed63, which only re-runs the repo's generators (gen:system-context-census, tenant-audit-census.mjs --write). Merge base 0253192f7 → 3fdb92db0.
  • Re-run by the seat on the committed trees (dev report 6114842061):
    • git diff --name-only 0253192f7 0f1906217 and git diff --name-only 3fdb92db0 78c3eed63 list the same 49 paths.
    • Path by path, the added and removed lines (git diff -U0, hunk headers dropped) differ on exactly 3 paths, all of them generator output:
      • content/docs/permissions/system-context.mdx (merge=os-regen): one census figure, main's base 129 → 130, with the PR's −1 unchanged;
      • content/docs/permissions/tenant-audit-census.mdx and docs/audits/2026-08-tenant-audit-write-call-sites.counts.md: the generator's "Measured on … at" stamp only, 81ec7792b → be55432f6.
    • Every other path's change is byte-identical.
  • So the at-tier PASS 6114282872 on 0f1906217a carries to 78c3eed638 as a pure regeneration. The landing check still reads this head's own CI.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · PR #22875(#15205,ADR-0131 C4 第二阶段)

domain:services seat 2 (#21118) · session_013LbZ9MhPp1iriZEtgJqioA · 2026-10-11T23:28Z · 审阅的提交:78c3eed638

改了什么

  • 邮件模板改为发送时直接从元数据注册表读取(email_template),不再读 sys_email_template 表。
  • 内置的 6 个认证邮件改为由邮件插件自己声明的元数据。它们共 4 种语言、24 份,渲染结果与原来逐字节一致,有测试锁定。
  • 以下退役:
    • 启动时把内置模板种进表的逻辑;
    • 把声明的模板同步进表的启动扫描和实时投影器;
    • 按行读取的旧加载器;
    • 插件的 seedTemplates 和 templates 两个选项。
  • 在 Studio 里保存模板,下一封邮件就生效,不经投影,也不用重启。
  • @objectstack/plugin-email 的公开导出删掉 9 个、新增 2 个,changeset 逐一写明旧名字改用什么。表本身和表里的数据一行没动。

为什么改

风险与代价(含回滚)

席位意见

  • 建议批准,理由如下:
    • 实现与裁决 C 一致,24 份内置模板逐字节相同。
    • 至档合约复审第一次判 FAIL,原因是 Clause-② 声明写错,那是席位自己的错。修正后在 0f1906217 上 PASS(6114282872)。
    • 之后为解决和主干的冲突做了一次合并,席位在提交后的树上复核过:只有生成器输出变了,属纯重新生成(6114859685)。
    • CI 在 78c3eed638 上全绿。
  • 另有一件可以另行回复、不阻塞本 PR 的事:ADR-0087 的处置分类里,没有一项准确对应"删掉运行时导出、同时改动 packages/spec"这种改动。本 PR 和 stage 1 都按"不涉及元数据迁移"处理。可以把这种读法写进 ADR 附录,也可以新增第七类。

你要做的

  • 用 os-zhuang 或 hotlong 账户在这个 PR 上点 Approve。批准后,由席位走合并队列落地。

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 12, 2026 00:34
@os-zhuang
os-zhuang enabled auto-merge October 12, 2026 00:34
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit 7de0265 Oct 12, 2026
54 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-15205-registry-loader branch October 12, 2026 01:11
zhuangjianguo pushed a commit that referenced this pull request Oct 12, 2026
…9939 pass 4, S4)

The os-regen driver kept one side of the generated reference in the merge
of origin/main 7de0265; gen:schema then gen:docs re-derive it, carrying
both this branch's notify value slots and #22875's template describe.

Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF
Co-authored-by: Claude <noreply@anthropic.com>
marchtian pushed a commit that referenced this pull request Oct 12, 2026
Resolve content/docs/permissions/system-context.mdx: keep row 55b (this
branch) and main's row 58 rewording (#22875); counts re-derived with
pnpm gen:system-context-census.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015JgoXpZUGtNxnMDvRZsKzP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor(plugin-email): templates resolve the registry; the seed and the provenance stamp retire; organization-level editing is closed (ADR-0131 D6/D10)

3 participants