Skip to content

feat(spec): retire SendTemplateInput.org — the declared org-overlay tenant id nothing ever read (ADR-0049 enforce-or-remove) - #12114

Merged
os-litant merged 2 commits into
mainfrom
claude/issue-11832-retire-sendtemplate-org
Aug 25, 2026
Merged

feat(spec): retire SendTemplateInput.org — the declared org-overlay tenant id nothing ever read (ADR-0049 enforce-or-remove)#12114
os-litant merged 2 commits into
mainfrom
claude/issue-11832-retire-sendtemplate-org

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#11832

What

Removes SendTemplateInput.org from packages/spec/src/contracts/email-service.ts under ADR-0049 enforce-or-remove. The member was declared as "Tenant id for org-overlay resolution (when supported)" and had zero readers: @objectstack/plugin-email — the only IEmailService implementation — resolves templates on (name, locale) only, so a caller passing org got no org-overlay resolution and no error. The "(when supported)" hedge was the declaration admitting the gap. After #11741 landed organizationId beside it, the input carried two org-shaped keys of which one did nothing — exactly the shape that invites an AI author to pick the wrong one.

org is NOT replaced by organizationId: that member is the delivery row's tenant stamp (sys_email.organization_id pass-through, #11741 / Decision 2 of #11303) and opts into no overlay resolution. #11741's members remain open and untouched semantically; #11741 is not affected by this PR.

Premise re-verification (at claim, base 8d60dcd)

  • packages/spec/src/contracts/email-service.ts:185 still declared the member (PM-checked at 0b04839; re-verified).
  • Zero .org reads in packages/plugins/plugin-email/src (the only prose mention is a comment at template-loader.ts:69).
  • Zero org: writers at any sendTemplate() call site: service-messaging email channel, qa/dogfood, plugin-auth, examples/app-showcase, docs (content/docs/kernel/runtime-services/email-service.mdx has no bare org).
  • RenderTemplateInput (the render-only subset) never carried org.
  • Sibling objectui: zero SendTemplateInput references.
  • The member shipped in published @objectstack/spec@17.2.0 (dist/contracts/index.d.ts:6318 in the npm tarball), so this is a published-contract-member removal, not an unpublished trim.

Route

Contracts interface member (programmatic contract; nothing parses it at runtime) — so the retirement kit is the lighter half of the spec-property-retirement playbook:

  • Tombstone prose in the interface where the member sat (why removed, date, the organizationId semantic boundary). No retiredKey() tombstone and no guidance-map entry: there is no Zod surface to carry a parse-time prescription; the enforcement channel is the compiler.
  • Compile-time pin in email-service.test.ts: authoring org is an excess-property error via @ts-expect-error, bidirectional under check:test-typecheck (re-adding the member turns the directive into TS2578).
  • D3 SemanticMigrationsend-template-input-org-retired (major 18) as a one-file shard entries/semantic/18.send-template-input-org-retired.ts + gen:migration-registry — conflict-free by construction with the parallel registry cards this batch (ActionSchema documents "newTabUrl is only valid together with opensInNewTab" but no refine enforces the pair (observation) #11842 / spec: two ComponentPropsMap entries contradict their ruled authorities — object-grid.data vs ViewDataSchema, element:number.filter vs the platform filter form (rulings ui#6207-A, ui#6206-B) #12039); semantic shards carry no sequence number to claim. No D2 conversion: the key only ever appeared in a call-time input bag — no metadata seam ever runs on it (the data.engine.update options.upsert precedent). spec-changes.json / docs/protocol-upgrade-guide.md are byte-unchanged by construction (projections stop at PROTOCOL_MAJOR 17; verified by running both generators — zero diff), matching every other staged major-18 entry.
  • Changeset@objectstack/spec: minor with BREAKING prose per the lockstep launch-window convention (the PR feat(spec): retire the nine dead contributes members and kinds[].globs (family: #10724 + #11169) #12001 house style), FROM → TO, and adr-0087: registered send-template-input-org-retired. runtime-interface-only is structurally refused for symbols under packages/spec/src/contracts/ and the body carries a prescription, so registered is the only honest disposition — gate verdict: "1 declared-breaking changeset(s), each carrying an ADR-0087 disposition … registered send-template-input-org-retired (new here)".
  • No consumer cleanup needed: zero writers measured (above), so no call site changes anywhere.
  • Liveness ledger untouched: the ledger walks authorable metadata schema shapes; a contracts interface member has no ledger surface (check:liveness green confirms).

Declared deviation from the claim's file surface

The claim said organizationId untouched. SendEmailInput.organizationId is untouched byte-for-byte. SendTemplateInput.organizationId's TSDoc, however, ended with "Distinct from {@link SendTemplateInput.org} …" — a link left dangling by this removal that would keep describing the retired member as existing. That one clause is rewritten to reference the retired member in past tense; the member's semantics and every other TSDoc sentence are unchanged. Flagged here rather than left as a dangling reference.

Verification — union re-run at final head 6a8a947 (post origin/main merge; delta vs main is exactly the 5 intended files, +156/−5)

Tests (all via the shared verify lock):

  • @objectstack/spec vitest src/contracts/email-service.test.ts src/migrations/migrations.test.ts: 2 files / 109 tests passed; full pnpm --filter @objectstack/spec typecheck green incl. check:test-typecheck: OK (test layer compiles; 55/263 ledger-held, unchanged).
  • @objectstack/plugin-email (downstream consumer of the rebuilt narrowed dist): vitest 27 files / 429 tests passed; tsc --noEmit clean.
  • @objectstack/example-showcasetest/email-template-locale.test.ts: 1 file / 6 tests passed.

Reverse verification (committed-state, mutation proven on disk by anchored grep both legs; tsc reads src via tsconfig.test.json, no dist leg involved): re-adding org?: string moves the test program from 263 ledger-held diagnostics to 264, the +1 being exactly src/contracts/email-service.test.ts(71,7): error TS2578: Unused '@ts-expect-error' directive; restore leg back to check:test-typecheck: OK at 263.

Gates at 6a8a947, each read from its own verdict line (exit captured before any pipe): check:generated "All 14 generated artifacts are up to date" (api-surface / export-origins / authorable-surface / docs unchanged — an interface-member removal is invisible to the existence-level snapshots, the expected reading for this route) · adr-0087 registration (verdict quoted above) · check:nul-bytes · check:merge-driver · check:published-files · check:slot-lookup · check:spec-parsed-alias · check:cross-package-test-inputs · check:changeset-gate-self-tests · check:objectui-changeset · check:query-options-erasure · check:test-source-alias · check:type-source-resolution · check:engine-double-contract · check:where-matcher · check:type-check-coverage · check-changeset-no-major ("no major bump") · check-ci-filter-parity · check-empty-changeset · check-plugin-teardown-shape · spec check:liveness / check:empty-state / check:variant-docs / check:strictness-ledger · lint check:doc-formula-expressions ("9 @example(s) judged clean across 1017 files") — all green.

Declared narrowings (accepted deviations; CI runs the farm):

  • check-dev-prereqs reds locally with "The workspace is not built — 1 unmet precondition, not a list of problems" (fresh targeted worktree; its own text says it is not a finding). CI builds first.
  • check:type-check-debt --re-measure not run repo-wide: its population is the DEBT/TEST_DEBT ledgers in scripts/check-type-check-coverage.mjs@objectstack/spec graduated (@ts-expect-error 退役 pin 在 packages/spec 里是幽灵检查:tsconfig 把 **/*.test.ts 排除出唯一的 tsc --noEmit #5286) and the @objectstack/spec-monorepo root program excludes packages/, so none of this PR's 5 paths is an input to any ledgered tsc program; the only programs reading them are spec's own three, run in full above.
  • Repo-wide pnpm lint / remaining farm: CI-owned.

Generated by Claude Code

…t id nothing ever read (#11832, ADR-0049 enforce-or-remove)
The member declared 'Tenant id for org-overlay resolution (when supported)'
and no implementation ever read it: plugin-email resolves templates on
(name, locale) only. Removal is a compile-time accept-set narrowing; runtime
behaviour is unchanged. organizationId (#11741) is untouched and is NOT a
replacement (delivery stamp, not overlay resolution).
- contracts/email-service.ts: member removed; tombstone prose in place;
stale cross-reference in SendTemplateInput.organizationId TSDoc updated
- email-service.test.ts: bidirectional compile pin (@ts-expect-error)
- D3 semantic entry send-template-input-org-retired (major 18, one-file
shard + gen:migration-registry; no D2 — call-time input bag, the
options.upsert precedent); spec-changes.json / upgrade guide unchanged
by construction (projections stop at PROTOCOL_MAJOR 17)
- changeset: @objectstack/spec minor (lockstep launch-window convention),
adr-0087: registered
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests tooling labels Aug 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 2 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/kernel/runtime-services/email-service.mdx(via SendTemplateInput (symbol))
What this run could not see
  • 5 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: 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.

Coarse fallback — 126 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 2ba4329e441a5ea66656efb41c1782de9380f0e3packageMentionDocs.

Which tree this was computed on

This run read content/docs from d11173b35468c9a1efb279fb87dbcb2b587ab4ae — the merge of head 6a8a947b6727eadacc5000a9306518cb7a49e4f5 into base 2ba4329e441a5ea66656efb41c1782de9380f0e3, 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 d11173b35468c9a1efb279fb87dbcb2b587ab4ae && git checkout d11173b35468c9a1efb279fb87dbcb2b587ab4ae
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2ba4329e441a5ea66656efb41c1782de9380f0e3 6a8a947b6727eadacc5000a9306518cb7a49e4f5 && git checkout -B drift-repro 2ba4329e441a5ea66656efb41c1782de9380f0e3 && git merge --no-ff 6a8a947b6727eadacc5000a9306518cb7a49e4f5
node scripts/docs-audit/affected-docs.mjs --json 2ba4329e441a5ea66656efb41c1782de9380f0e3

⚠️ 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 2ba4329e441a5ea66656efb41c1782de9380f0e3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] SendTemplateInput.org is declared ("Tenant id for org-overlay resolution (when supported)") but no implementation reads it

2 participants

@os-litant@claude