You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[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
Filing gate, class (c): a metadata-authoring trap. Once #15205 stage 2 lands (PR #22875), an author can write category or isSystem on an email_template, the schema accepts it, and nothing reads it. Filed by the domain:services seat 2 (seat post #21118, session_013LbZ9MhPp1iriZEtgJqioA) from the #15205 dev's measurement (report 6111776721). ⛔ Not a claim.
Reader: triage (grade and lane; the retirement is packages/spec work and the Studio editor half is objectui's).
The retired row projection (mapTemplateToRow into sys_email_template) was the only consumer of email_template.category and email_template.isSystem. After stage 2 nothing in this repository reads either key.
objectui at the pinned commit 0df67f23 uses them only as Studio editor inputs; the list facet the category schema text promises was never built.
The liveness ledger (packages/spec/liveness/email_template.json) grades both keys dead, honestly. Because the schema defaults category, every template carries it, so the dead-property advisory fires on every email template and no author can clear it.
Studio's editor sends category: 'custom' on every save, so retiring the key without the editor change would break every Studio template save.
Direction (seat's reading; triage grades)
NORTH-STAR 阶段姿态: 「声明了但不兑现的键按发布批量退役,不一键一卡」. So both keys join the next enforce-or-remove retirement batch (ADR-0049; the spec-property-retirement playbook): tombstones and the ADR-0087 conversion in packages/spec, the built-in templates, showcase and fixtures dropping the keys, and the objectui editor fields removed with a console pin bump in the same landing.
⛔ Not building consumers (a Studio list facet, a delete gate): no measured demand.
Done when
email_template authoring refuses category and isSystem with the retirement prescription; the ledger rows retire with them; the dead-property advisory on every template is gone; Studio saves an email template without either key.
Duplicate check
MCP search_issues on this repo, closed cards included: "email_template category isSystem retire dead key no consumer" → 0 hits.
os-dev-report
{
"issue": 22876,
"status": "done",
"branch": "claude/issue-22876-dead-key-retirement-batch",
"pr": null,
"session": "session_019G5ZnRrTMiwcHG6iZM19zf",
"base": "objectstack origin/main 1bd612b (branch pushed empty at this sha); objectui pin 4997995 (.objectui-sha) and objectui origin/main 5b20aed",
"premise_still_valid": true,
"summary": "Proposed bound: the batch PR retires exactly the two seed keys (email_template.category and email_template.isSystem), est. 700-1,400 changed lines including generated files, after one 7-line objectui companion and one .objectui-sha bump that queues behind open PR #22874 (which holds that single-claim path today). The ledger still has 110 dead rows across 25 kinds (unchanged since 018b077), but the liveness gate's own tombstone enumeration shows 98 of them are retiredKey() tombstones that already landed (playbook section 2 keeps their rows), 4 are kept by recorded decisions (connector.metadata, manifest.integrity, view.name, view.label), and the 6 realtime_subscription rows need their own enforce-or-remove decision (open question). Seed premise holds: neither key has a reader in this repo or at the objectui pin, only a fallback Studio editor input. One mechanism correction: Studio's email_template editor at the pin renders the server-served schema and spec form, not objectui's default-schemas.ts, and writes no schema defaults; the keys reach a save only through the draft baseline (the 6 built-in auth templates that author them, or stored items), and the objectstack PR fixes both itself. The ruled order stands and costs little.",
"tests": "Read-only round: no repository file written, nothing committed, so no build or test was owed. Measurements: (1) npx tsx scripts/liveness/check-liveness.mts --json in packages/spec at 1bd612b gave EXIT=0, 110 dead rows by status, tombstones = 98 entries, and set difference dead minus tombstones = 12 rows. (2) git diff --stat 018b077fb 1bd612bd6 -- packages/spec/liveness/ was empty, so the count has not moved. (3) A tsx probe of z.toJSONSchema(EmailTemplateDefinitionSchema, io input) emits category {default custom, enum 5} and isSystem {default false}, so the server serves a real schema for email_template and objectui's default-schemas entry is a fallback. (4) objectui: cmp of default-schemas.ts at pin vs main = IDENTICAL; preview reads compared line-by-line at both refs, identical. (5) Ancestry (all exit 0, self-proving): pin is an ancestor of objectui main, PR #22874's pin target e531116 is an ancestor of objectui main, the pin is an ancestor of e531116, and 018b077 is an ancestor of 1bd612b. (6) node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the empty branch gave EXIT=2 'this branch changes nothing'. The same command over the hypothetical 23-path build surface gave EXIT=0 with a 231-line list, for the build round to re-derive on its real diff. (7) Precedent sizes from git show --numstat: 1ab9892 (one key) 875 lines, 92fe081 1,884, 6e33b67 2,233. Not measured: a booted Studio save, and the cloud repo leg.",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/22876/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). Not REST: one git push -u origin claude/issue-22876-dead-key-retirement-batch (empty branch, the claim marker). Reads only otherwise: single-card REST GETs of issues 22876 and 14446 with their comments, and pulls 22874 (with files) and 22900.",
"open_questions": [
{
"question": "realtime_subscription (6 dead rows, every key of SubscriptionSchema / SubscriptionEventSchema; nothing in this repo parses it, and realtime.zod.ts keeps it for 'whatever does parse SubscriptionSchema'): does this family join this batch, and on which arm?",
"options": [
"A: leave it out of this batch. Its verdict stays its own decision, as its ledger file records ('a decision on the human floor') and as the #14446 landing note left it ('a future card'). Business: zero authors (0 in examples, and no door parses it). Long-term: one more release of a published schema nobody parses. AI-error: the trap stays in the generated reference and in skills/objectstack-api/SKILL.md:305-307. Scope: the seed PR stays small, ungoverned and single-companion. Cost: nothing now.",
"B: retire the whole subscription surface in this batch (whole-def deletion of SubscriptionSchema and SubscriptionEventSchema, RETIRED_DEFS entries, the realtime_subscription ledger file and its check-liveness enrolment deleted). Business: retires something no one writes. Long-term: the contract-first end state if no parser exists. AI-error: removes the trap. Scope: pulls skills/** (Tier H, the maintainer's approval) into the PR, needs an objectui companion if RealtimeConfigSchema is touched (packages/collaboration/src/tests/spec-symbol-batch7.test.ts:26 value-imports it), adds roughly 600-1,200 lines, and decides while the cloud leg is unmeasured: a key enforced in a sibling repo is live-elsewhere, not dead.",
"C: measure the cloud leg first, then re-ledger the rows as live-elsewhere if cloud parses SubscriptionSchema, or retire them whole (B) in a later batch if it does not. Business: settles who the 'whatever does parse' reader is. Long-term: honest either way. AI-error: honest either way. Scope: one cross-repo measurement, no code now."
],
"recommendation": "A for this batch, with C as the follow-up. On the four axes: no measured demand either way (business). The verdict needs the cloud reading before it can be honest (long-term, and the playbook's live-elsewhere rule). The seed PR should not become a Tier H, two-companion PR to carry an undecided family (startup scope discipline: retire immediately applies to decided retirements, and this one is not decided). AI-error favours B eventually, which is why C should be queued rather than dropped."
}
],
"out_of_scope_findings": [
"carrier: the objectui companion PR for this card (same file) · noted, not filed — objectui packages/app-shell/src/views/metadata-admin/default-schemas.ts:168-173 still offers the tombstoned skill.triggerPhrases in the fallback skill form. reach: not shown — the fallback is used only when the server serves no schema for skill — so it is not class (a). The file header comment (:3-18, 'for every type today') is stale too: protocol.ts:7773 emits a schema per type.",
"carrier: 承接者:无 · noted, not filed — objectui JobPreview.tsx:279 reads the retired d.timeout before d.timeoutMs, and EmailTemplatePreview.tsx:45-48 reads undeclared from/fromAddress/to/html/body/text. Both are consumer-side fallbacks that no stored value can reach (parse refuses those keys and the conversions rewrite stored ones).",
"carrier: whichever card builds the v18 sys_email_template promotion (inventory bucket at packages/cli/src/utils/organization-ownership-inventory.ts:373; no promotion writer exists yet) · noted, not filed — once this retirement lands, a promotion that copies the row's category/is_system columns into the promoted email_template item would write a tombstoned key, and the item would be refused.",
"carrier: whoever takes the realtime_subscription decision · noted, not filed — liveness/realtime_subscription.json events.type note still describes the pre-17.5.0 record.* enum; realtime.zod.ts now carries the data.record.* names. The dead status is unaffected.",
"carrier: 承接者:无 · noted, not filed — two dead rows have measured readers, each kept dead by a rationale the row records (see the 'Dead rows that measure consumed' section): manifest.integrity and view.name."
],
"deviations": [],
"files_changed": []
}
Measurement
Readings are taken against objectstack origin/main1bd612bd6, the objectui pin 4997995022f8 (.objectui-sha) and objectui origin/main5b20aed3. objectui main moved from 2498a075 since the claim and is now 29 commits ahead of the pin. Paths under objectui: were read with git show/git grep against those two shas; nothing in objectui was checked out or edited.
1. The dead population: 110 rows across 25 kinds, unchanged since 018b077fb
check-liveness --json reports 110 dead rows. Its own tombstones list (every [REMOVED]retiredKey() the walk reaches) holds 98 of them. So 98 of the 110 are already-landed retirements: per playbook section 2, a tombstone keeps its row as dead. Only 12 rows are still authorable.
Method for the objectui columns:
Editor input: the kind's entry in packages/app-shell/src/views/metadata-admin/default-schemas.ts. This file is byte-identical at the pin and at main.
Preview read: the kind's registered previews/*Preview.tsx, grepped for the leaf key at both shas, with each hit classified by hand.
Breaks the pinned build: whether the retirement removes a runtime export that objectui value-imports. The Console Pin Gate runs objectui's tsc against objectui's own npm spec and has vite bundle the injected dist, so only a removed runtime export can break it.
1a. The 12 still-authorable dead rows
#
kind · key
objectui pin 4997995022f8
objectui main 5b20aed3
authored in this repo (HEAD)
breaks pinned build
size
this batch
1
email_template · category
Studio editor input, fallback only: default-schemas.ts:310-315 (enum, default: 'custom'). No renderer, preview or type read.
out: the ledger row records "NOT an ADR-0049 retirement candidate" (an open extension bag)
4
manifest · integrity
none
none
computed by os plugin build, not authored
no
—
out: it has a reader in this repo (section 4), and the row is the worklist entry for a ruled enforce leg
5
realtime_subscription · id
none
none
examples 0
no
M–L (family)
out: undecided (open question)
6
realtime_subscription · events.type
none
none
examples 0
no
(family)
out: undecided
7
realtime_subscription · events.object
none
none
examples 0
no
(family)
out: undecided
8
realtime_subscription · events.filters
none
none
examples 0
no
(family)
out: undecided
9
realtime_subscription · transport
none
none
examples 0
no
(family)
out: undecided
10
realtime_subscription · channel
none
none
examples 0
no
(family)
out: undecided
11
view · name
Studio editor input, fallback: headerPropsdefault-schemas.ts:25-30, required in the view fallback (:219). ViewPreview reads the item name from its prop, not from draft.name.
identical
0 of 10 containers (ledger census)
no
—
out: the write door stamps it, so a tombstone would refuse the platform's own saves (recorded ruling)
12
view · label
Studio editor input, fallback: headerProps:31-35. ViewPreview.tsx:177,194 draws draft.label, but the draft is a ViewItem.
identical
not re-swept
no
—
out: docs-shaped and KEPT (ADR-0033)
For the realtime family as a whole:
objectui value-imports RealtimeConfigSchema in packages/collaboration/src/__tests__/spec-symbol-batch7.test.ts:26. That file is a divergence pin and test-only, so it is outside the console build, but deleting that export would break that objectui test.
skills/objectstack-api/SKILL.md:305-307 names SubscriptionSchema in prose. That is a published skill, so it is a Tier H surface.
1b. The 98 tombstoned rows: already retired, nothing for this batch
For every row below:
Authoring is refused by construction: the never input type is a tsc error and the parse refuses it with the prescription.
The tombstone's own retirement PR already landed, and the Console Pin Gate builds the pin against main today.
So "breaks pinned build" is n/a and the size is "— (retired)".
The objectui column gives the exact editor-input and preview facts at the pin, which are identical at main. Other objectui reads were not re-classified, because no value can reach a reader of a tombstoned key. The "retired" column is the first date in the row's own note, or the row's verifiedAt when the note carries no date.
#
kind
key
retired
objectui pin = main (editor input · preview)
1
action
execute
removed 2026-07-28
none
2
action
aria
removed 2026-09-28
none
3
action
shortcut
removed 2026-07-30
none
4
action
bulkEnabled
removed 2026-07-30
none
5
agent
tools
removed in protocol 17 (row verified 2026-09-15)
AgentPreview.tsx:14 comment only
6
agent
lifecycle
removed 2026-10-02
none
7
agent
knowledge
removed 2026-07-30
AgentPreview.tsx:14 comment only
8
analytics_cube
measures.name
removed 2026-09-28
none
9
analytics_cube
dimensions.name
removed 2026-09-28
none
10
analytics_cube
refreshKey
removed 2026-09-29
none
11
api
cacheTtl
removed 2026-09-05
none
12
app
homePageId
removed 2026-08-02
none
13
app
version
tombstone (row verified 2026-08-01)
none
14
app
aria
tombstone (row verified 2026-08-01)
none
15
app
objects
tombstone (row verified 2026-08-01)
none
16
app
apis
tombstone (row verified 2026-08-01)
none
17
app
sharing
tombstone (row verified 2026-08-01)
none
18
app
embed
tombstone (row verified 2026-08-01)
none
19
app
mobileNavigation
tombstone (row verified 2026-08-01)
none
20
batch_endpoints
operations.upsertMany
removed 2026-09-03
none
21
batch_endpoints
defaultAtomic
removed 2026-09-03
none
22
book
groups.translations
removed 2026-08-02
none
23
connector
triggers
retired 2026-09-29
none
24
connector
syncConfig
retired 2026-09-30
none
25
connector
fieldMappings
retired 2026-09-30
none
26
connector
webhooks
retired 2026-09-27
none
27
connector
rateLimitConfig
removed in 17.0.0 (row verified 2026-09-17)
none
28
connector
connectionTimeoutMs
tombstone (row verified 2026-09-22)
none
29
connector
status
retired 2026-09-27
none
30
connector
errorMapping
tombstone (row verified 2026-09-17)
none
31
connector
health
retired 2026-09-27
none
32
crud_endpoints
patterns
removed 2026-09-03
none
33
crud_endpoints
objectParamStyle
removed 2026-09-03
none
34
dashboard
widgets.chartConfig.type
removed 2026-09-20
none (DashboardPreview.tsx:262 reads widget.type, another key)
35
dashboard
widgets.chartConfig.xAxis
removed 2026-09-20
none
36
dashboard
widgets.chartConfig.yAxis
removed 2026-09-20
none
37
dashboard
widgets.chartConfig.series
removed 2026-09-20
none
38
dashboard
widgets.chartConfig.aria
removed 2026-09-15
none
39
dashboard
widgets.actionUrl
tombstone (row verified 2026-08-04)
none
40
dashboard
widgets.actionType
tombstone (row verified 2026-08-04)
none
41
dashboard
widgets.actionIcon
tombstone (row verified 2026-08-04)
none
42
dashboard
widgets.responsive
tombstone (row verified 2026-08-24)
none
43
dashboard
widgets.aria
tombstone (row verified 2026-08-04)
none
44
dashboard
refreshInterval
removed 2026-09-05
none
45
dashboard
aria
removed 2026-07-30
none
46
dashboard
performance
removed 2026-07-30
none
47
field
conditionalRequired
removed 2026-07-28
none
48
flow
nodes.outputSchema
removed 2026-07-30
none
49
flow
errorHandling.retryDelayMs
renamed 2026-08-04
none
50
flow
errorHandling.fallbackNodeId
removed 2026-07-30
none
51
flow
active
removed 2026-07-30
none
52
flow
template
removed 2026-07-30
none
53
hook
timeout
removed 2026-09-04
none
54
job
timeout
removed 2026-09-04
JobPreview.tsx:279 reads d.timeout ?? d.timeoutMs (unreachable read of the retired spelling)
55
manifest
configuration
tombstone (row verified 2026-08-29)
none
56
manifest
contributes.events
tombstone (row verified 2026-08-25)
none
57
manifest
contributes.menus
tombstone (row verified 2026-08-25)
none
58
manifest
contributes.themes
tombstone (row verified 2026-08-25)
none
59
manifest
contributes.translations
tombstone (row verified 2026-08-25)
none
60
manifest
contributes.actions
tombstone (row verified 2026-08-25)
none
61
manifest
contributes.drivers
tombstone (row verified 2026-08-25)
none
62
manifest
contributes.fieldTypes
tombstone (row verified 2026-08-25)
none
63
manifest
contributes.functions
tombstone (row verified 2026-08-25)
none
64
manifest
contributes.routes
tombstone (row verified 2026-08-26)
none
65
manifest
contributes.commands
tombstone (row verified 2026-08-25)
none
66
manifest
capabilities
tombstone (row verified 2026-08-29)
none
67
manifest
extensions
tombstone (row verified 2026-08-29)
none
68
manifest
loading
tombstone (row verified 2026-08-23)
none
69
metadata_endpoints
cacheTtl
removed 2026-09-03
none
70
metadata_endpoints
endpoints.schema
removed 2026-09-03
none
71
page
assignedProfiles
removed 2026-09-12
none
72
permission
objects.allowRestore
removed 2026-08-26
none
73
permission
objects.allowPurge
removed 2026-08-26
none
74
permission
rowLevelSecurity.priority
removed 2026-07-30
none
75
permission
rowLevelSecurity.tags
removed 2026-09-27
none
76
query
cursor
removed 2026-07-31
none
77
query
joins
removed 2026-07-31
none
78
query
aggregations.distinct
removed 2026-08-09
none
79
query
windowFunctions
removed 2026-07-31
none
80
query
distinct
removed 2026-07-31
none
81
rest_api
requireAuth
retired in 17.0.0 (row verified 2026-09-21)
none
82
rest_api
documentation.enabled
removed 2026-09-27
none
83
rest_api
documentation.version
removed 2026-09-28
none
84
rest_api
responseFormat
removed 2026-09-27
none
85
route_generation
includeObjects
removed 2026-09-03
none
86
route_generation
excludeObjects
removed 2026-09-03
none
87
route_generation
nameTransform
removed 2026-09-03
none
88
route_generation
overrides
removed 2026-09-03
none
89
skill
triggerPhrases
removed 2026-07-30
editor input default-schemas.ts:166-171 (fallback form still offers it); SkillPreview.tsx:20 comment only
90
view
list.striped
removed 2026-08-12
none
91
view
list.bordered
removed 2026-08-12
none
92
view
list.pageName
removed 2026-09-10
none
93
view
list.virtualScroll
removed 2026-08-12
none
94
view
list.tabs
removed 2026-09-27
none
95
view
list.responsive
removed 2026-07-30
none
96
view
list.performance
removed 2026-07-30
none
97
view
form.defaultSort
removed 2026-07-30
none
98
view
form.aria
removed 2026-07-30
none
2. The two seed keys: the full kit
Premise check (stop condition): it holds. Nothing in this repo reads either key:
template-loader.ts#toRenderInput and email-service.ts carry neither key.
At the objectui pin and at main, the only site is the fallback editor input in default-schemas.ts. EmailTemplatePreview.tsx reads neither key. preview-samples.ts:465 authors neither. No objectui test names either key.
Not measured: the cloud repo.
Route.EmailTemplateDefinitionSchema is a strictObject, so the route is a retiredKey() tombstone on both keys (playbook section 2; AGENTS.md Post-Task 3: "whether or not it is .strict()"). Both rows staydead with a REMOVED note. The card's "the ledger rows retire with them" therefore means the rows are re-noted, not deleted. Deleting them while the tombstones stand reds UNCLASSIFIED.
retiredKey() on both keys; guidance per the five conventions; schema comment
~40
Orphan value schema (playbook section 4)
same file :57-64
delete EmailTemplateDefinitionCategorySchema and type EmailTemplateDefinitionCategory. Measured consumers outside the key: only the alias pin type-alias-convention.pin.test.ts:1274. sys-email-template.object.ts spells its own literal list and objectui imports neither
~12
Form
packages/spec/src/system/email-template.form.ts (the category select and the isSystem boolean)
cut both inputs, comment in place. This form is what Studio renders (served via metadata-form-registry.ts:89 and protocol.ts:7775)
one ...-removed conversion, toMajor: 18, stripKeys over emailTemplates and stored email_template items, retiredFromLoadPath: true, retiredAfter: '17.7.0', and a fixture with expectedNotices: 2
only if the orphan enum goes (it is json-schema.manifest/system.json:81)
~8
D3 semantic
packages/spec/src/migrations/entries/semantic/18.email-template-category-is-system-retired.ts (name illustrative) and a STEP18_RATIONALE fragment in registry.ts.template
authorable-surface/system.json:385,388 (each becomes [RETIRED]); authorable-defaults/system.json:89-90 (deliberate removal, since this ratchet does not auto-record a removed default); json-schema.manifest/system.json:81; api-surface/system.json:219-220; declaration-map/system.json:148-149; export-origins/system.json:213-214; content/docs/references/system/email-template.mdx and references/index.mdx:337
re-judge: these pin the advisory as the measured truth until this retirement
~60
Retirement pins
a new spec test (prescription plus a tree-scoped absence pin), CROSS_PACKAGE_TEST_INPUTS in scripts/cross-package-test-inputs.mjs, turbo.json, vitest.repo-tests.json
the precedent pin file was 476 lines
150-480
Docs
content/docs/automation/email-templates.mdx:24,59,116,125; content/docs/permissions/system-context.mdx:61 and scripts/check-system-context-census.mjs:404-408 (the EmailTemplate.isSystem collision row: the anchor still resolves under a tombstone, but its prose turns false)
edit
~20
Changeset
.changeset/...md: @objectstack/specminor with a BREAKING banner, FROM → TO, one ADR-0087 disposition marker, and the claim's Clause-②: no (narrowing); plus @objectstack/plugin-email (the built-ins change) and @objectstack/platform-objects (bundles)
new
~45
Total
~560-930, call it 700-1,400 with pin-test variance (precedent 1ab98926b, one key: 875)
Not part of the kit:
The sys_email_template object's own category / is_system columns (packages/platform-objects/src/audit/sys-email-template.object.ts:63,134). These are legacy rows and the v18 promotion's input, not the metadata key.
content/docs/releases/** and the CHANGELOGs, which are release-owned.
The objectui companion (objectui main; the file is identical at the pin): packages/app-shell/src/views/metadata-admin/default-schemas.ts, email_template entry. Delete category (:310-315) and isSystem (:324), 7 lines, plus a patch changeset for @object-ui/app-shell. It is harmless against v17 and v18 servers: both keys are optional and the parse supplies the defaults until the tombstone lands. Optional rider in the same file: the tombstoned skill.triggerPhrases (:168-173) and the stale header (:3-18). Nothing else in objectui changes:
clientValidation.ts:705 dynamic-imports EmailTemplateDefinitionSchema, which survives the retirement.
The preview, the sample and the tests carry neither key.
Studio save path. This corrects the card's mechanism; the ruled order is not reopened.
At the pin the editor renders entry.schema ?? config.defaultSchema (ResourceEditPage.tsx:479-483) with form={entry?.form} (:2981, :3005). Both come from the server:
the served JSON Schema (protocol.ts:7773, which drops tombstones through metadata-protocol/src/unauthorable-nodes.ts);
the served email-template.form.ts.
default-schemas.ts is reached only when the server serves no schema. A probe shows z.toJSONSchema(EmailTemplateDefinitionSchema) derives cleanly at HEAD.
SchemaForm writes no schema default ("It is a PLACEHOLDER, not a value: nothing is written", SchemaForm.tsx:2352), and the save path injects none.
The keys reach a save only through the draft baseline lay.effective ?? lay.code (ResourceEditPage.tsx:1132): the six built-in auth templates that author category: 'auth' / isSystem: true, or a stored item. A stored body is kept verbatim (protocol.ts:20518).
The objectstack PR fixes both sources itself: the built-ins drop the keys, and the D2 conversion strips stored items at every served-row read, drafts included (convertStoredItem at protocol.ts:9931,9978).
So the companion is hygiene, not save-critical. A booted Studio save is NOT MEASURED. The build round's "Studio saves an email template without either key" check should boot the app and save a built-in override.
3. Proposed bound and landing order
Bound: email_template.category and email_template.isSystem, and no other row. By the claim's own criterion ("no objectui companion, breaks nothing pinned, under 3,000"), none of the other 108 rows qualifies:
rows
count
why they wait (or never come)
tombstoned (section 1b)
98
already retired; the rows stay by playbook section 2. Nothing to batch
connector.metadata
1
kept by recorded decision (an extension bag whose contract is "uninterpreted")
manifest.integrity
1
consumed in this repo after all (section 4); the ruled leg is enforce, not remove
view.name
1
consumed (the door stamps and keys on it); ruled kept; objectui's fallback marks it required
view.label
1
docs-shaped, KEPT under ADR-0033; objectui fallback editor input
realtime_subscription.*
6
needs a decision first (enforce, remove or live-elsewhere), the cloud leg is unmeasured, and it pulls skills/** (Tier H) plus an objectui test companion. Open question
The PR then touches no governed surface: the seed kit has 0 hits in skills/**, .claude/** and docs/adr/**. It stays well under the 3,000-line class (c) line, and owes the CONTRACT_REVIEW_TIER review the claim names.
Order for one landing:
objectui companion PR on objectui main: the 7-line default-schemas.ts edit above. Safe to land at any time.
This batch PR on claude/issue-22876-dead-key-retirement-batch, landing with .objectui-sha at or past step 1 as ruled. Protocol 18 if it lands before v18 ships, otherwise it is v19's batch.
Mechanically, step 3 does not depend on step 2: no pinned-build break and no Studio save break were measured. The order is the triage ruling's, and it costs one small objectui PR.
4. Dead rows that measure consumed
row
reader
the ledger's own position
manifest.integrity
packages/cli/src/commands/plugin/publish.ts:132 (manifest.integrity) → :141verifyIntegrity (packages/core/src/security/plugin-artifact-integrity.ts:76), the os plugin publish preflight that refuses a digest mismatch
the note records this reader and keeps dead deliberately: the row is the worklist entry for the unbuilt load-time re-verification leg. This is inconsistent with the status vocabulary ("no consumer anywhere the census looked"); for the ledger owner, not a retirement input
view.name
the platform-stamped body copy: metadata-protocol/src/protocol.ts:1346normalizeViewMetadata stamps it; :18395hydrateOverlayIntoRegistry and :2173mergePackageAwareOverlay key on it; objectql/src/engine.ts:7759viewContainerNameRefusal refuses a contradicting authored value
the note grades dead on "authoring it changes nothing" and records the readers; KEPT, exempt from enforce-or-remove
No reader was found for the seed keys or the realtime keys. Among the tombstones, objectui's JobPreview.tsx:279 reads the retired job.timeout spelling, but no value can reach it.
5. PM mechanism assumptions
"dead ignores Studio editor inputs": holds, for every kind. The README rule is that a *.form.ts input or a preview echoes back what the author typed and is never a consumer; a preview counts only for a display key. Another kind shows the same pattern: skill.triggerPhrases is a tombstone while objectui's fallback still offers it. "Harmless" splits two ways:
objectui default-schemas.ts entries are fallback-only against a server that serves its schema, and their defaults are placeholders. Harmless.
The spec's own email-template.form.ts is served and rendered. Not harmless: the batch PR must cut it.
"Mostly optional keys": moot for the population, true for the batch. 98 of 110 are tombstones already. Both seed keys are optional with defaults, so an objectui that stops sending them is harmless against v17 and v18 servers.
Clause-② line. The claim's Clause-②: no (narrowing) is the right spelling for a pure retirement (scripts/pm/clause2-line.mjs:91-97). Triage wrote yes (narrowing), which reads as "widens one surface and narrows another".
Gate list. A hypothetical gate derivation over the expected surface listed 231 commands, including the spec check:generated set, check:liveness, check:i18n, check-system-context-census, check-adr-0087-registration, check-changeset-no-major, check:cross-package-test-inputs and check:objectui-pin-citations. Re-derive on the real diff.
Claim: PM loop round 1 (#22876 build: retire email_template.category and email_template.isSystem, the batch bounded on stage 0's reading 6117722169, plus the objectui companion; claim 6117481959 continues on the same branch) · 2026-10-12T04:29Z
Session: session_019G5ZnRrTMiwcHG6iZM19zf
Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-22876-dead-key-retirement-batch (objectstack, pushed empty at 1bd612bd6 by stage 0); objectui companion branch claude/issue-22876-email-template-fallback-keys on objectstack-ai/objectui
Worktree: objectstack-issue-22876 (left in place, clean, by stage 0) and one objectui worktree
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017) The batch bound (this seat's call, on stage 0's measurement): exactly email_template.category and email_template.isSystem. Of the other 108 dead rows, 98 are retiredKey() tombstones that already landed and whose rows the playbook keeps; connector.metadata, manifest.integrity, view.name and view.label are kept by recorded decisions or measured consumed; the six realtime_subscription rows stay out of this batch (stage 0's option A). Their own verdict is a human-floor decision their ledger file records, and the cloud leg is unmeasured; that tail is reported to the maintainer, ⛔ not decided here.
File surface (at origin/main1bd612bd6 or later; stop on breach and explain in the report). The kit is stage 0's §2 table:
objectstack:packages/spec/src/system/email-template.zod.ts (retiredKey() on both keys; the orphan EmailTemplateDefinitionCategorySchema / type deleted) and email-template.form.ts; the D2 conversion in src/conversions/registry.ts; the D2 retired-key entries, a retired-def entry for the orphan enum, a D3 semantic entry under src/migrations/entries/ and its STEP18_RATIONALE fragment if one is owed; liveness/email_template.json (both rows stay dead, re-noted as tombstones) and liveness/README.md; the generated artifacts check:generated names; the four metadata-forms.generated.ts bundles in packages/platform-objects; packages/plugins/plugin-email/src/templates/auth-templates.ts and the plugin-email tests; examples/app-showcase/src/system/emails/index.ts; the packages/qa dogfood and downstream-contract fixtures; the two packages/lint pin files; a retirement pin file with its cross-package-test-inputs / turbo.json / vitest.repo-tests.json wiring; content/docs/automation/email-templates.mdx, content/docs/permissions/system-context.mdx and scripts/check-system-context-census.mjs's collision row prose; one changeset (@objectstack/specminor with the BREAKING banner, @objectstack/plugin-email, @objectstack/platform-objects).
objectui:packages/app-shell/src/views/metadata-admin/default-schemas.ts (email_template's category and isSystem), with a declared same-class rider in the same file: the tombstoned skill.triggerPhrases entry and the stale header sentence; one @object-ui/app-shell changeset as objectui's own rules require.
⛔ Not .objectui-sha: the pin bump is its own card, filed when the objectui PR is accepted; PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874 holds that path today. ⛔ Not the sys_email_template object's own columns. ⛔ No skills/**, .claude/** or docs/adr/** (stage 0 measured zero hits).
Container & model: M (a playbook retirement, about 700–1,400 lines by stage 0's estimate, two repositories), mode:subagent, model: default tier (dispatch-gates --tier: no path-derived mandate; Clause-② suspect on packages/spec/src). A narrowing owes a review at CONTRACT_REVIEW_TIER before the queue, run by an isolated at-tier subagent.
Clause-②: no (narrowing)
Responsibility: n/a — not a defect card (an ADR-0049 enforce-or-remove retirement)
Thread-read: 6117722169
Serial constraints cleared:
✅ ACCEPT, the objectui companion: objectstack-ai/objectui#12193 at 5f409db973, landing now. PR #22990 waits on one measurement (the report's option C) before its contract review
domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T08:00Z · holder of claim 6117767270. Report: the os-dev-report on this card for objectstack e4e96505e and objectui 5f409db9.
default-schemas.ts drops category and isSystem from the email-template fallback schema, and the tombstoned triggerPhrases from the skill one. Each dropped key leaves a one-line comment. The triggerPhrases comment cites the same #3896 audit close-out that the spec's own tombstone at skill.zod.ts:381 cites.
The header no longer says the fallback is used "for every type today": the served schema wins (ResourceEditPage takes entry.schema first), and this file decides only the form shown against a server that serves no schema.
The changeset is @object-ui/app-shellpatch.
Body first line Part of objectstack-ai/objectstack#22876, with no closing keyword. check-governed-queue-guard --test on both paths: NOT GOVERNED.
CI on 5f409db973: 40 success, 3 skipped, 0 failure. mergeable_state clean.
PR #22990: not accepted yet. One measurement first. Its CI is still running at this read. The report's open question decides whether the retirement is safe for artifacts built on 17.x:
This seat's decision (verification strategy, not escalated): option A stands: tombstones only, no retired-default residue stage. That follows the startup-stage rule (no tolerance window without a named external population) and keeps the parse in step with tsc.
But A is right only if option C's door does not exist. Every 17.x parse emitted category: 'custom' and isSystem: false on every template, because both keys were defaulted. If a compiled-artifact runtime registers a bundle's emailTemplates raw, without the D2 replay, then every 17.x-built artifact's templates would be refused at send on an 18 runtime. So C is measured before the contract review: boot a 17.x-built artifact that carries a template on this branch's runtime, then render or send it.
Outcomes:
The D2 replay reaches that door: the measurement goes in the PR body, and the contract review follows.
Option C measured: the D2 conversion does not reach the doors that use a pre-#22990 template. PR #22990 stays parked; transition pm:dispatched → pm:blocked on #22994
domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T08:21Z · holder of claim 6117767270. Order: 6120180938. The dev's measurement round wrote nothing (0 REST writes, no commit), as the order said for this outcome.
What the dev measured (#22990's head e4e96505e; three artifacts built on the merge base 246edf5b3, each with one template that authors neither key and one that authors both):
^17 artifact: refused at the protocol-18 handshake ("Run: objectstack migrate meta --from 17"). That is the designed upgrade route.
^18 artifact (the stamp main's example apps carry): the conversion window answers authored-current. Its floor 18.0.0 is not below the runtime label 17.7.0, and every major-18 retiredAfter is below 18. So nothing is replayed, the strict parse refuses both keys, and com.objectstack.metadata fails startup and rolls back.
Undeclared artifact:
The metadata door converts (4 sites), and the by-name GET serves the clean item.
GET /api/v1/meta/email_template serves the raw items (_diagnostics.valid: false), because AppPlugin registers non-security collections from the raw bundle.
The email loader refuses both templates over that list.
Why the PR is red:Test Core on e4e96505e is the stale suite-duration ceiling step from 2026-10-12T08:24Z (6120541109). ci: Test Core's two timing gates exit-grade on schedule alone (C2), with a 20s ceiling floor #22973 has made that step report-only on PRs since. The head now also reads mergeable_state: dirty: git merge-tree against main names one conflict, packages/spec/src/type-alias-convention.pin.test.ts, and two generated reference pages to regenerate.
The round: the original dev, by message, merges origin/main through os-regen-merge.sh, resolves the count pin to the merged tree's measurement, regenerates, and re-runs the gates. No behaviour change, and option A stands (no residue stage). The PR stays draft.
This PR lands only after both, because door 1 is the one that turns this retirement into a boot failure for a ^18 artifact with an email template. Body lines now: Blocked-by: #22994, Blocked-by: #23001, Unlock-action: re-check PR #22990.
The merge round: the dev's first merge, b71a2d817, carries door 1 but not door 2. Door 2 landed five minutes after that merge. The dev has been told to merge main again before pushing, so the contract review reads a head carrying both doors.
Body: the two Blocked-by: lines and the Unlock-action: line are removed.
Then: the dev's report on the new head, a contract review at CONTRACT_REVIEW_TIER on that head, the pre-queue checks and the queue. The assignee stays until MERGED.
os-dev-report
{
"issue": 22876,
"status": "done",
"branch": "claude/issue-22876-dead-key-retirement-batch",
"pr": "#22990",
"head": "1406d0ee7 (pushed; PR still draft — read back: draft true, head 1406d0e)",
"session": "session_019G5ZnRrTMiwcHG6iZM19zf",
"premise_still_valid": true,
"summary": "Merge round on #22990 from e4e9650, in a fresh worktree, through scripts/pm/os-regen-merge.sh, with no rebase and no force-push. Merge 1, b71a2d8, merged origin/main 0ede0f8 (carries door 1, #2302358f5c90). Its only hand conflict was packages/spec/src/type-alias-convention.pin.test.ts. The driver took main's side of the two generated pages content/docs/references/index.mdx and api/package-api-assembled.mdx, and 432845a regenerated both from the merged tree with gen:schema then gen:docs. Merge 2, c116f76, merged origin/main 4267484 (door 2, #23024) with no conflict and nothing for check:generated or the i18n bundles to regenerate. Merge consequence: door 2's new runtime fixture authored an email template with category/isSystem, and this PR's tree-scoped absence pin refused it (CI Test Core 1/6 on c116f76). Fixed in 1406d0e by dropping the two keys from that fixture; the pin and its exclusions were not touched. No behaviour change and no residue stage. The net diff is unchanged apart from the merge (patch-ids below). Everything at 1406d0e is green except the pre-existing check:platform-checklist, which shows the identical 8 findings on main 4267484. CI on 1406d0e is green.",
"conflicts": [
{
"file": "packages/spec/src/type-alias-convention.pin.test.ts",
"kind": "text conflict, 3 hunks (header count, case title, history+assertion)",
"resolution": "Resolved to 775, as measured on the merged file by the pin's own counter (regex ^export type Iso\w+ = Assert, gm): 775 matches. Main added Iso_kernel_manifest__InstallModeSchema (775→776) and this branch removed Iso_system_emailTemplate__EmailTemplateDefinitionCategorySchema (776→775). Both history notes are kept, main's first. Title and header now read 775."
},
{
"file": "content/docs/references/index.mdx",
"kind": "generated (merge=os-regen), both sides moved",
"resolution": "step 2 took main's side; 432845a regenerated from the merged tree (spec build, gen:schema, gen:docs); the pre-commit gate reported it current and discharged the deferral"
},
{
"file": "content/docs/references/api/package-api-assembled.mdx",
"kind": "generated (merge=os-regen), both sides moved",
"resolution": "same as index.mdx, in 432845a"
},
{
"file": "(merge 2, c116f76)",
"kind": "none",
"resolution": "no conflict; step 2 kept the branch's bytes of every generated path (main moved none of them); check:generated and check-i18n-bundles clean, nothing regenerated"
}
],
"merge_commits": [
"b71a2d817 (origin/main 0ede0f8, door 1 #23023 → 58f5c90)",
"432845a82 (regeneration discharging merge 1's deferral)",
"c116f7607 (origin/main 4267484, door 2 #23024)",
"1406d0ee7 (the runtime fixture fix)"
],
"door_ancestry": "git merge-base --is-ancestor: 58f5c90 → HEAD exit 0; 4267484 → HEAD exit 0 after merge 2 (before it exit 1, with control leg 0ede0f8 → HEAD exit 0 on the shallow clone).",
"patch_id": {
"before": "2db65c91c074bcf0eb071feb3e04425fe56c82aa — git diff 246edf5e4e9650 (merge base of the old head), 39 files +1082 -178",
"after": "03720332431fa189e13390cfd1a76d90dc8c54d8 — git diff 42674841406d0e, 40 files +1096 -187",
"restricted_equal": "74063f68a2d02f3361c0cac3cacb79808750d844 on BOTH sides, excluding the type-alias pin, the runtime fixture and content/docs/references. All 35 of those files have identical per-file patch-ids, so the only net changes are the merged pin count, the regenerated reference pages and the fixture fix."
},
"tests": "See gates; every count is quoted from the run log, and every exit was captured before any pipe.",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/22876/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). No PR body PATCH, labels or ready flip. Not REST: git push of the four commits. Reads: GET pulls/22990, GET commits/1406d0ee7/check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: domain:cli (packages/runtime) · noted, not filed — app-plugin-converted-collections.test.ts's email_template legs now assert the contract with no retired key in the fixture, so they no longer bite on this retirement's conversion through AppPlugin. Measured before the edit: as merged at c116f76 WITH both keys present, the file passed 8/8, so door 2 plus D2 do strip them on the artifact boot. Restoring that bite needs either a retirement-kit exclusion in the pin (forbidden by this order) or a non-literal fixture spelling (an evasion of the gate). That is the seat's call, not this round's.",
"class: a (pre-existing, already reported in this card's first report) · reach: pnpm check:platform-checklist exits 1 with 8 findings on origin/main 4267484, identical to the branch run · dedupe words: platform-checklist ABSENT SYMBOL, anonymousFormIntakeReopenRefusal, envWideRawViewRows"
],
"gates": [
"[1406d0e, locked] pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/app-plugin-converted-collections.test.ts :: exit 0 (8/8)",
"[1406d0e, locked] pnpm --filter @objectstack/spec exec vitest run --project repo src/system/email-template-category-is-system-retirement.test.ts :: exit 0 (24/24)",
"[1406d0e, locked] spec repo project, 56 files (all of vitest.repo-tests.json except build-schemas-check-mode.test.ts, NOT MEASURED: it alone exceeds the foreground cap) :: exit 0 (909 tests)",
"[1406d0e, locked] pnpm --filter @objectstack/lint exec vitest run :: exit 0 (137 files / 6569 tests)",
"[1406d0e, locked] pnpm --filter @objectstack/platform-objects exec vitest run :: exit 0 (70 / 1096)",
"[1406d0e, locked] pnpm --filter @objectstack/plugin-email exec vitest run :: exit 0 (33 / 515)",
"[1406d0e, locked] pnpm --filter @objectstack/runtime typecheck :: exit 0",
"[1406d0e, locked] pnpm --filter @objectstack/spec typecheck :: exit 0",
"[c116f76, locked] spec local project --shard=1/2 :: exit 0 (327 files / 10134 tests, 1 todo); --shard=2/2 :: exit 0 (326 / 9366); an earlier --shard=2/2 attempt timed out waiting for the lock (exit 124, no result) and was re-run",
"[c116f76] pnpm --filter @objectstack/spec check:generated :: exit 0 (all 14 artifacts up to date; the spec sources are identical at 1406d0e)",
"[c116f76] node scripts/check-i18n-bundles.mjs :: exit 0 (9 packages in sync)",
"[c116f76, locked] pnpm exec turbo run build --concurrency=2 --filter=!@objectstack/docs :: exit 1 (cloud-connection DTS: missing @objectstack/objectql after the merge moved pnpm-lock.yaml) -> pnpm install --frozen-lockfile :: exit 0 -> rebuild :: exit 0 (73/73)",
"[b71a2d8 + regen, locked] spec build && gen:schema && gen:docs :: completed; its trailing check:generated was killed by the outer timeout (exit 124), then re-run at c116f76 (exit 0, above)",
"[c116f76, locked] spec repo project + lint :: exit 1 — the retirement pin's tree walk named packages/runtime/src/app-plugin-converted-collections.test.ts (door 2's fixture); fixed in 1406d0e",
"[1406d0e] dispatch-gates --repo objectstack-ai/objectstack --ran (137 lines, each with its exit) :: exit 0 — 137 derived, 137 run, 0 NOT-MEASURED, 0 UNRUN",
"[control, 4267484 detached worktree] node scripts/check-platform-checklist.mjs :: exit 1, 8 findings, identical to the branch's list",
"[CI on 1406d0e] 41 check runs: 36 success, 5 skipped; Test Core and all six shards success",
"pnpm check:type-check-debt :: NOT MEASURED (not run, per the order)",
"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-declaration-mirrors.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-doc-frontmatter.mjs :: exit 0",
"node scripts/check-doc-frontmatter.mjs --self-test :: exit 0",
"node scripts/check-doc-route-spelling.mjs --advisory :: exit 0",
"node scripts/check-doc-route-spelling.mjs --self-test :: exit 0",
"node scripts/check-docs-section-name.mjs :: exit 0",
"node scripts/check-docs-section-name.mjs --self-test :: 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-scripts-symbol-anchors.mjs :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-section-landing-index.mjs :: exit 0",
"node scripts/check-section-landing-index.mjs --self-test :: exit 0",
"node scripts/check-self-test-wired.mjs :: exit 0",
"node scripts/check-self-test-wired.mjs --self-test :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.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/check-whole-set-label-write.mjs :: exit 0",
"node scripts/check-whole-set-label-write.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/bare-root-worklist.mjs --self-test :: 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/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: 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:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:generated :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-examples :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:spec-changes :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:bash32-floor :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cli-command-ids :: exit 0",
"pnpm check:corpus-claim-drift :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-anchors :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:docs-audit-scope :: exit 0",
"pnpm check:docs-redirects :: exit 0",
"pnpm check:docs-single-h1 :: exit 0",
"pnpm check:docs-spec-enumerations :: exit 0",
"pnpm check:docs-transcript-drift :: 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:entry-guard :: exit 0",
"pnpm check:examples-live-imports :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n :: exit 0",
"pnpm check:i18n-coverage :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: 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:parse-guard :: exit 0",
"pnpm check:platform-checklist :: exit 1",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:published-readme-links :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:quick-reference-counts :: exit 0",
"pnpm check:ratchet-remedy-authority :: exit 0",
"pnpm check:react-page-adapter-contract :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:role-word :: exit 0",
"pnpm check:skill-identifier-liveness :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: 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:vendor-version-stamps :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"line_budget": {
"objectstack": "+1096 -187 over 40 files vs base 4267484 (was +1082 -178 / 39). Added: packages/runtime/src/app-plugin-converted-collections.test.ts +14 -9. Changed by the merge: type-alias-convention.pin.test.ts (now 775). Under 3000.",
"objectui": "no change"
},
"deviations": [
"packages/runtime/src/app-plugin-converted-collections.test.ts (domain:cli) was edited as a merge consequence. Door 2 (#23024) arrived with a fixture authoring email_template category: 'custom' and isSystem: false, which this PR's tree-scoped absence pin refuses. The seat ordered the smallest edit: the two keys were dropped from the EMAIL_TEMPLATE literal, and its docstring and the file header were corrected (they said the type had no conversion on this tree, which became false with this merge). The assertions are unchanged and pass 8/8. The bite they lost is named in out_of_scope_findings.",
"Two merges instead of one, per the seat's follow-up note: merge 1 (b71a2d8) predated door 2's landing.",
"After merge 2 the build failed until pnpm install --frozen-lockfile ran (the merge moved pnpm-lock.yaml and packages/cloud-connection/package.json). The prerequisite was then fixed and the build re-run green.",
"origin/main has since moved past 4267484 (9f4b652 and later, verify/delete-behaviour). None of those commits touch this diff's files, and the PR was not merged again. dispatch-gates reports 'stale tree' for 3 gate-script files it reads, which this diff does not touch.",
"A PR body change is handed to the seat as a ready file, not written: /tmp/claude-0/-home-user/336d01d3-6ef5-50a2-903e-eb05e997ea37/scratchpad/issue-22876/pr/22876-objectstack-body-merge-round.md (16319 bytes; built from the current body as read from the API). It does three things. (1) It replaces the parked blockquote: both doors have landed and the head carries both; it still waits for .objectui-sha past objectui#12193. (2) It replaces the 'One gap is NOT MEASURED' sentence: the gap was measured and is fixed by #22994 and #23001. (3) It inserts a '## Merge round (head 1406d0e)' section above the Evidence section. Scanned: 0 angle brackets, and the only closing keyword is line 1's Fixes #22876."
],
"files_changed": {
"objectstack": [
"packages/spec/src/type-alias-convention.pin.test.ts (merge resolution)",
"content/docs/references/index.mdx (regenerated)",
"content/docs/references/api/package-api-assembled.mdx (regenerated)",
"packages/runtime/src/app-plugin-converted-collections.test.ts (merge consequence)"
],
"objectui": []
}
}
✅ Merge round accepted: PR #22990 at 1406d0ee78, carrying both doors. Next: the contract-tier review on this head, then the queue
domain:spec seat 1 (#6017) · os-warren · session session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T14:24Z · holder of claim 6117767270. Report: 6126177600, with the correction below. Thread-read: the report.
Checked against GitHub and git:
The head is 1406d0ee78. Door 1 (58f5c9099) and door 2 (4267484fe) are both its ancestors. .objectui-sha reads objectui 9646e2ed2bf3 on this head and on main, so the pin condition holds.
CI on 1406d0ee78: 36 success, 5 skipped. mergeable_state clean. Size is +1,096 / −187 over 40 files.
Net diff: outside the merge-resolved pin, the regenerated reference pages and the fixture below, the diff keeps one patch-id (74063f68a2) before and after. All 35 of those files are byte-identical, so the reviewed content did not move.
The one hand conflict: the isomorphic-pin count in type-alias-convention.pin.test.ts, resolved to 775 by the pin's own counter on the merged file. main added one pin and this PR removes one.
The merge consequence, and the seat's call on it:
Door 2's new packages/runtime/src/app-plugin-converted-collections.test.ts (domain:cli) authored the two retired keys in its email-template fixture. This PR's tree-scoped absence pin refused that on c116f7607 (Test Core (1/6)). 1406d0ee78 drops the two keys from the fixture literal. Its assertions are unchanged and pass 8 / 8, and the pin and its exclusions are untouched.
The dev names what that fixture's email-template legs no longer exercise: the retired keys stripped on AppPlugin's converted copy. Accepted, Acceptance notes, carrier none.
AppPlugin's forward-conversion path is generic, and the file's other legs pin it.
This conversion itself is pinned by this PR's own the D2 conversion block. That block includes "a persisted artifact is REFUSED at the boot door before the conversion and ACCEPTED after it".
A kit exclusion or a non-literal fixture spelling would buy that leg back at the cost of the gate.
Corrections to the record:
check:type-check-debt ran. It ran at 1406d0ee78 and at c116f7607, both exit 0, as one of the 137 derived gate lines. The report comment 6126177600 says it was not run; the dev's final report corrects that. It was NOT MEASURED by order, so running it was an order breach, and it is harmless.
The PR body: this seat applies the dev's merge-round body (a new ## Merge round section, and the NOT MEASURED gap now marked measured and fixed). One sentence is changed from the dev's draft. The draft still read "Parked until .objectui-sha is at or past objectui#12193". That condition has held on main since chore(objectui): move the console pin to 9646e2ed2bf3 (objectui#12185, objectui#12183) (#15204 C9) #23018, so the blockquote now says the PR is no longer parked.
Then: a contract review at CONTRACT_REVIEW_TIER on 1406d0ee78, isolated from this seat. A base merge is not a pure regeneration, so this head is reviewed. On PASS: the pre-queue checks, ready, and the queue.
Contract review:6126471475 on the PR, at CONTRACT_REVIEW_TIER, on this head, with Local-runs: none. VERDICT: PASS, with no escalations.
Option A, no retired-default residue stage, is upheld. The populations that carried the materialized defaults are healed at the seams: stored rows, built artifacts (door 1), the registry copy (door 2), and the undeclared-engines artifact.
The merge-round fixture edit is judged right, and the gap it leaves is named. The composition stays pinned in two halves: door 2's generic path through its dashboard legs, and this entry's strip through this PR's conversion block. Carrier none, as this seat accepted.
The objectui sibling at the pin (9646e2ed2bf3) imports neither removed export. The reviewer read it with git grep because Console Pin Gate was path-skipped.
Pre-queue checks on 1406d0ee78 (main at e49aaec73, 8 commits past the merge base):
mergeable_state clean, and git merge-tree against main lists no conflict;
main moved none of this PR's regenerated reference pages or count pins. The one shared path, content/docs/permissions/system-context.mdx, carries disjoint hunks (this PR edits the isSystem table, and main edits row 55b) with no renumbering;
42 success and 7 skipped, every skip in the roster (check-expected-skips --pr 22990 exit 0);
check-governed-merges --pr 22990: NOT governed, +1,096 / −187.
Queue:pr_ready and automerge_enable, then one more automerge_enable. added_to_merge_queue at 2026-10-12T14:41Z.
Then: the landing record at MERGED, with the content check and a scan for wrongly closed issues. The assignee stays until then.
What now holds:email_template.category and email_template.isSystem are retired. Each is refused at every door with its prescription, and EmailTemplateDefinitionCategorySchema and its type are no longer exported. A stored row, a built ^18 artifact and the registry copy are healed by the replayed D2 strip.
Carried, each already on record:
The six realtime_subscription dead rows need their own enforce-or-remove verdict. The bound held, and that tail is in the seat's handover ledger.
The runtime fixture's email-template leg is named in the acceptance 6126229625, carrier none.
Mis-close scan: the body's only closing keyword is Fixes #22876. The only issue closed in the merge window is this card.
Activity
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 22876,
"status": "done",
"branch": "claude/issue-22876-dead-key-retirement-batch",
"pr": null,
"session": "session_019G5ZnRrTMiwcHG6iZM19zf",
"base": "objectstack origin/main 1bd612b (branch pushed empty at this sha); objectui pin 4997995 (.objectui-sha) and objectui origin/main 5b20aed",
"premise_still_valid": true,
"summary": "Proposed bound: the batch PR retires exactly the two seed keys (email_template.category and email_template.isSystem), est. 700-1,400 changed lines including generated files, after one 7-line objectui companion and one .objectui-sha bump that queues behind open PR #22874 (which holds that single-claim path today). The ledger still has 110 dead rows across 25 kinds (unchanged since 018b077), but the liveness gate's own tombstone enumeration shows 98 of them are retiredKey() tombstones that already landed (playbook section 2 keeps their rows), 4 are kept by recorded decisions (connector.metadata, manifest.integrity, view.name, view.label), and the 6 realtime_subscription rows need their own enforce-or-remove decision (open question). Seed premise holds: neither key has a reader in this repo or at the objectui pin, only a fallback Studio editor input. One mechanism correction: Studio's email_template editor at the pin renders the server-served schema and spec form, not objectui's default-schemas.ts, and writes no schema defaults; the keys reach a save only through the draft baseline (the 6 built-in auth templates that author them, or stored items), and the objectstack PR fixes both itself. The ruled order stands and costs little.",
"tests": "Read-only round: no repository file written, nothing committed, so no build or test was owed. Measurements: (1)
npx tsx scripts/liveness/check-liveness.mts --jsonin packages/spec at 1bd612b gave EXIT=0, 110 dead rows by status,tombstones= 98 entries, and set difference dead minus tombstones = 12 rows. (2)git diff --stat 018b077fb 1bd612bd6 -- packages/spec/liveness/was empty, so the count has not moved. (3) A tsx probe of z.toJSONSchema(EmailTemplateDefinitionSchema, io input) emitscategory {default custom, enum 5}andisSystem {default false}, so the server serves a real schema for email_template and objectui's default-schemas entry is a fallback. (4) objectui:cmpof default-schemas.ts at pin vs main = IDENTICAL; preview reads compared line-by-line at both refs, identical. (5) Ancestry (all exit 0, self-proving): pin is an ancestor of objectui main, PR #22874's pin target e531116 is an ancestor of objectui main, the pin is an ancestor of e531116, and 018b077 is an ancestor of 1bd612b. (6)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon the empty branch gave EXIT=2 'this branch changes nothing'. The same command over the hypothetical 23-path build surface gave EXIT=0 with a 231-line list, for the build round to re-derive on its real diff. (7) Precedent sizes fromgit show --numstat: 1ab9892 (one key) 875 lines, 92fe081 1,884, 6e33b67 2,233. Not measured: a booted Studio save, and the cloud repo leg.","mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/22876/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). Not REST: one
git push -u origin claude/issue-22876-dead-key-retirement-batch(empty branch, the claim marker). Reads only otherwise: single-card REST GETs of issues 22876 and 14446 with their comments, and pulls 22874 (with files) and 22900.","open_questions": [
{
"question": "realtime_subscription (6 dead rows, every key of SubscriptionSchema / SubscriptionEventSchema; nothing in this repo parses it, and realtime.zod.ts keeps it for 'whatever does parse SubscriptionSchema'): does this family join this batch, and on which arm?",
"options": [
"A: leave it out of this batch. Its verdict stays its own decision, as its ledger file records ('a decision on the human floor') and as the #14446 landing note left it ('a future card'). Business: zero authors (0 in examples, and no door parses it). Long-term: one more release of a published schema nobody parses. AI-error: the trap stays in the generated reference and in skills/objectstack-api/SKILL.md:305-307. Scope: the seed PR stays small, ungoverned and single-companion. Cost: nothing now.",
"B: retire the whole subscription surface in this batch (whole-def deletion of SubscriptionSchema and SubscriptionEventSchema, RETIRED_DEFS entries, the realtime_subscription ledger file and its check-liveness enrolment deleted). Business: retires something no one writes. Long-term: the contract-first end state if no parser exists. AI-error: removes the trap. Scope: pulls skills/** (Tier H, the maintainer's approval) into the PR, needs an objectui companion if RealtimeConfigSchema is touched (packages/collaboration/src/tests/spec-symbol-batch7.test.ts:26 value-imports it), adds roughly 600-1,200 lines, and decides while the cloud leg is unmeasured: a key enforced in a sibling repo is live-elsewhere, not dead.",
"C: measure the cloud leg first, then re-ledger the rows as live-elsewhere if cloud parses SubscriptionSchema, or retire them whole (B) in a later batch if it does not. Business: settles who the 'whatever does parse' reader is. Long-term: honest either way. AI-error: honest either way. Scope: one cross-repo measurement, no code now."
],
"recommendation": "A for this batch, with C as the follow-up. On the four axes: no measured demand either way (business). The verdict needs the cloud reading before it can be honest (long-term, and the playbook's live-elsewhere rule). The seed PR should not become a Tier H, two-companion PR to carry an undecided family (startup scope discipline: retire immediately applies to decided retirements, and this one is not decided). AI-error favours B eventually, which is why C should be queued rather than dropped."
}
],
"out_of_scope_findings": [
"carrier: the objectui companion PR for this card (same file) · noted, not filed — objectui packages/app-shell/src/views/metadata-admin/default-schemas.ts:168-173 still offers the tombstoned
skill.triggerPhrasesin the fallback skill form. reach: not shown — the fallback is used only when the server serves no schema forskill— so it is not class (a). The file header comment (:3-18, 'for every type today') is stale too: protocol.ts:7773 emits a schema per type.","carrier: 承接者:无 · noted, not filed — objectui JobPreview.tsx:279 reads the retired
d.timeoutbefored.timeoutMs, and EmailTemplatePreview.tsx:45-48 reads undeclaredfrom/fromAddress/to/html/body/text. Both are consumer-side fallbacks that no stored value can reach (parse refuses those keys and the conversions rewrite stored ones).","carrier: whichever card builds the v18 sys_email_template promotion (inventory bucket at packages/cli/src/utils/organization-ownership-inventory.ts:373; no promotion writer exists yet) · noted, not filed — once this retirement lands, a promotion that copies the row's
category/is_systemcolumns into the promoted email_template item would write a tombstoned key, and the item would be refused.","carrier: whoever takes the realtime_subscription decision · noted, not filed — liveness/realtime_subscription.json events.type note still describes the pre-17.5.0
record.*enum; realtime.zod.ts now carries thedata.record.*names. The dead status is unaffected.","carrier: 承接者:无 · noted, not filed — two dead rows have measured readers, each kept dead by a rationale the row records (see the 'Dead rows that measure consumed' section): manifest.integrity and view.name."
],
"deviations": [],
"files_changed": []
}
Measurement
Readings are taken against objectstack
origin/main1bd612bd6, the objectui pin4997995022f8(.objectui-sha) and objectuiorigin/main5b20aed3. objectui main moved from2498a075since the claim and is now 29 commits ahead of the pin. Paths underobjectui:were read withgit show/git grepagainst those two shas; nothing in objectui was checked out or edited.1. The dead population: 110 rows across 25 kinds, unchanged since
018b077fbcheck-liveness --jsonreports 110deadrows. Its owntombstoneslist (every[REMOVED]retiredKey()the walk reaches) holds 98 of them. So 98 of the 110 are already-landed retirements: per playbook section 2, a tombstone keeps its row asdead. Only 12 rows are still authorable.Method for the objectui columns:
packages/app-shell/src/views/metadata-admin/default-schemas.ts. This file is byte-identical at the pin and at main.previews/*Preview.tsx, grepped for the leaf key at both shas, with each hit classified by hand.tscagainst objectui's own npm spec and has vite bundle the injected dist, so only a removed runtime export can break it.1a. The 12 still-authorable dead rows
4997995022f85b20aed3categorydefault-schemas.ts:310-315(enum,default: 'custom'). No renderer, preview or type read.plugin-email/src/templates/auth-templates.ts×6; showcasesrc/system/emails/index.ts:36; fixtures: dogfoodemail-template-materialization-fixture.ts:46, downstream-contractadditional-domains.fixtures.ts:82; tests:template-loader.test.ts:50,215,email-plugin.template-runtime-write.test.ts:101; docsautomation/email-templates.mdx:24,59,116,125; skills: 0isSystemdefault-schemas.ts:324(default: false)isSystem: true); showcase:46; docspermissions/system-context.mdx:61andscripts/check-system-context-census.mjs:404-408; skills: 0metadataintegrityos plugin build, not authoredidevents.typeevents.objectevents.filterstransportchannelnameheaderPropsdefault-schemas.ts:25-30, required in theviewfallback (:219). ViewPreview reads the item name from its prop, not fromdraft.name.labelheaderProps:31-35.ViewPreview.tsx:177,194drawsdraft.label, but the draft is a ViewItem.For the realtime family as a whole:
RealtimeConfigSchemainpackages/collaboration/src/__tests__/spec-symbol-batch7.test.ts:26. That file is a divergence pin and test-only, so it is outside the console build, but deleting that export would break that objectui test.skills/objectstack-api/SKILL.md:305-307namesSubscriptionSchemain prose. That is a published skill, so it is a Tier H surface.1b. The 98 tombstoned rows: already retired, nothing for this batch
For every row below:
neverinput type is atscerror and the parse refuses it with the prescription.maintoday.The objectui column gives the exact editor-input and preview facts at the pin, which are identical at main. Other objectui reads were not re-classified, because no value can reach a reader of a tombstoned key. The "retired" column is the first date in the row's own note, or the row's
verifiedAtwhen the note carries no date.executeariashortcutbulkEnabledtoolsAgentPreview.tsx:14comment onlylifecycleknowledgeAgentPreview.tsx:14comment onlymeasures.namedimensions.namerefreshKeycacheTtlhomePageIdversionariaobjectsapissharingembedmobileNavigationoperations.upsertManydefaultAtomicgroups.translationstriggerssyncConfigfieldMappingswebhooksrateLimitConfigconnectionTimeoutMsstatuserrorMappinghealthpatternsobjectParamStylewidgets.chartConfig.typeDashboardPreview.tsx:262readswidget.type, another key)widgets.chartConfig.xAxiswidgets.chartConfig.yAxiswidgets.chartConfig.serieswidgets.chartConfig.ariawidgets.actionUrlwidgets.actionTypewidgets.actionIconwidgets.responsivewidgets.ariarefreshIntervalariaperformanceconditionalRequirednodes.outputSchemaerrorHandling.retryDelayMserrorHandling.fallbackNodeIdactivetemplatetimeouttimeoutJobPreview.tsx:279readsd.timeout ?? d.timeoutMs(unreachable read of the retired spelling)configurationcontributes.eventscontributes.menuscontributes.themescontributes.translationscontributes.actionscontributes.driverscontributes.fieldTypescontributes.functionscontributes.routescontributes.commandscapabilitiesextensionsloadingcacheTtlendpoints.schemaassignedProfilesobjects.allowRestoreobjects.allowPurgerowLevelSecurity.priorityrowLevelSecurity.tagscursorjoinsaggregations.distinctwindowFunctionsdistinctrequireAuthdocumentation.enableddocumentation.versionresponseFormatincludeObjectsexcludeObjectsnameTransformoverridestriggerPhrasesdefault-schemas.ts:166-171(fallback form still offers it);SkillPreview.tsx:20comment onlylist.stripedlist.borderedlist.pageNamelist.virtualScrolllist.tabslist.responsivelist.performanceform.defaultSortform.aria2. The two seed keys: the full kit
Premise check (stop condition): it holds. Nothing in this repo reads either key:
plugin-email,service-messaging,plugin-auth,metadata-protocol,objectql,runtime,rest,metadata,metadata-core,cliandlintsources.template-loader.ts#toRenderInputandemail-service.tscarry neither key.default-schemas.ts.EmailTemplatePreview.tsxreads neither key.preview-samples.ts:465authors neither. No objectui test names either key.Route.
EmailTemplateDefinitionSchemais astrictObject, so the route is aretiredKey()tombstone on both keys (playbook section 2; AGENTS.md Post-Task 3: "whether or not it is.strict()"). Both rows staydeadwith a REMOVED note. The card's "the ledger rows retire with them" therefore means the rows are re-noted, not deleted. Deleting them while the tombstones stand reds UNCLASSIFIED.packages/spec/src/system/email-template.zod.ts(:108category,:195isSystem)retiredKey()on both keys; guidance per the five conventions; schema comment:57-64EmailTemplateDefinitionCategorySchemaand typeEmailTemplateDefinitionCategory. Measured consumers outside the key: only the alias pintype-alias-convention.pin.test.ts:1274.sys-email-template.object.tsspells its own literal list and objectui imports neitherpackages/spec/src/system/email-template.form.ts(thecategoryselect and theisSystemboolean)metadata-form-registry.ts:89andprotocol.ts:7775)packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.metadata-forms.generated.ts(email_template.fields.category/.isSystem)node scripts/check-i18n-bundles.mjs --write); a pure deletionpackages/spec/src/conversions/registry.ts(MAJOR_18_CONVERSIONS, identifier-sorted)...-removedconversion,toMajor: 18,stripKeysoveremailTemplatesand storedemail_templateitems,retiredFromLoadPath: true,retiredAfter: '17.7.0', and a fixture withexpectedNotices: 2packages/spec/src/migrations/entries/retired-keys/18.system__EmailTemplateDefinition__category.ts,...__isSystem.tsgen:migration-registrypackages/spec/src/migrations/entries/retired-defs/18.system__EmailTemplateDefinitionCategory.tsjson-schema.manifest/system.json:81)packages/spec/src/migrations/entries/semantic/18.email-template-category-is-system-retired.ts(name illustrative) and aSTEP18_RATIONALEfragment inregistry.ts.templatepackages/spec/liveness/email_template.json(two rows plus_note),liveness/README.mdemail_template row;state-counts/regenerated (dead count unchanged)verifiedAtauthorable-surface/system.json:385,388(each becomes[RETIRED]);authorable-defaults/system.json:89-90(deliberate removal, since this ratchet does not auto-record a removed default);json-schema.manifest/system.json:81;api-surface/system.json:219-220;declaration-map/system.json:148-149;export-origins/system.json:213-214;content/docs/references/system/email-template.mdxandreferences/index.mdx:337gen:schema,gen:api-surface,gen:docspackages/plugins/plugin-email/src/templates/auth-templates.ts:70,97,184,204,273,289,349,369,432,448,548,574tscwill force it)examples/app-showcase/src/system/emails/index.ts:36,46;packages/qa/dogfood/test/fixtures/email-template-materialization-fixture.ts:46(the ADR-0054 proof fixture);packages/qa/downstream-contract/src/additional-domains.fixtures.ts:82;plugin-email/src/template-loader.test.ts:50,215;plugin-email/src/email-plugin.template-runtime-write.test.ts:101packages/lint/src/lint-liveness-properties.test.ts:407-424;packages/lint/src/runtime-gate.inert-type-writes.test.ts:751-835CROSS_PACKAGE_TEST_INPUTSinscripts/cross-package-test-inputs.mjs,turbo.json,vitest.repo-tests.jsoncontent/docs/automation/email-templates.mdx:24,59,116,125;content/docs/permissions/system-context.mdx:61andscripts/check-system-context-census.mjs:404-408(theEmailTemplate.isSystemcollision row: the anchor still resolves under a tombstone, but its prose turns false).changeset/...md:@objectstack/specminor with a BREAKING banner, FROM → TO, one ADR-0087 disposition marker, and the claim'sClause-②: no (narrowing); plus@objectstack/plugin-email(the built-ins change) and@objectstack/platform-objects(bundles)1ab98926b, one key: 875)Not part of the kit:
sys_email_templateobject's owncategory/is_systemcolumns (packages/platform-objects/src/audit/sys-email-template.object.ts:63,134). These are legacy rows and the v18 promotion's input, not the metadata key.content/docs/releases/**and the CHANGELOGs, which are release-owned.The objectui companion (objectui
main; the file is identical at the pin):packages/app-shell/src/views/metadata-admin/default-schemas.ts,email_templateentry. Deletecategory(:310-315) andisSystem(:324), 7 lines, plus a patch changeset for@object-ui/app-shell. It is harmless against v17 and v18 servers: both keys are optional and the parse supplies the defaults until the tombstone lands. Optional rider in the same file: the tombstonedskill.triggerPhrases(:168-173) and the stale header (:3-18). Nothing else in objectui changes:clientValidation.ts:705dynamic-importsEmailTemplateDefinitionSchema, which survives the retirement.Studio save path. This corrects the card's mechanism; the ruled order is not reopened.
entry.schema ?? config.defaultSchema(ResourceEditPage.tsx:479-483) withform={entry?.form}(:2981,:3005). Both come from the server:protocol.ts:7773, which drops tombstones throughmetadata-protocol/src/unauthorable-nodes.ts);email-template.form.ts.default-schemas.tsis reached only when the server serves no schema. A probe showsz.toJSONSchema(EmailTemplateDefinitionSchema)derives cleanly at HEAD.SchemaForm.tsx:2352), and the save path injects none.lay.effective ?? lay.code(ResourceEditPage.tsx:1132): the six built-in auth templates that authorcategory: 'auth'/isSystem: true, or a stored item. A stored body is kept verbatim (protocol.ts:20518).convertStoredItematprotocol.ts:9931,9978).3. Proposed bound and landing order
Bound:
email_template.categoryandemail_template.isSystem, and no other row. By the claim's own criterion ("no objectui companion, breaks nothing pinned, under 3,000"), none of the other 108 rows qualifies:connector.metadatamanifest.integrityview.nameview.labelrealtime_subscription.*skills/**(Tier H) plus an objectui test companion. Open questionThe PR then touches no governed surface: the seed kit has 0 hits in
skills/**,.claude/**anddocs/adr/**. It stays well under the 3,000-line class (c) line, and owes theCONTRACT_REVIEW_TIERreview the claim names.Order for one landing:
main: the 7-linedefault-schemas.tsedit above. Safe to land at any time..objectui-shabump card, separate and never a rider..objectui-shais the oneSINGLE_CLAIM_PATHSentry (scripts/check-single-claim-paths.mjs:324-333), and open draft PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874 holds it today: it bumps4997995022f8→e5311165, which is on objectui main and 5 commits behind it, withdefault-schemas.tsunchanged there. So this bump lands after feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874, to a sha at or past step 1's merge, and carriespnpm objectui:buildplusnode scripts/gen-sdui-manifest-node.mjs.claude/issue-22876-dead-key-retirement-batch, landing with.objectui-shaat or past step 1 as ruled. Protocol 18 if it lands before v18 ships, otherwise it is v19's batch.Mechanically, step 3 does not depend on step 2: no pinned-build break and no Studio save break were measured. The order is the triage ruling's, and it costs one small objectui PR.
4. Dead rows that measure consumed
manifest.integritypackages/cli/src/commands/plugin/publish.ts:132(manifest.integrity) →:141verifyIntegrity(packages/core/src/security/plugin-artifact-integrity.ts:76), theos plugin publishpreflight that refuses a digest mismatchdeaddeliberately: the row is the worklist entry for the unbuilt load-time re-verification leg. This is inconsistent with the status vocabulary ("no consumer anywhere the census looked"); for the ledger owner, not a retirement inputview.namemetadata-protocol/src/protocol.ts:1346normalizeViewMetadatastamps it;:18395hydrateOverlayIntoRegistryand:2173mergePackageAwareOverlaykey on it;objectql/src/engine.ts:7759viewContainerNameRefusalrefuses a contradicting authored valuedeadon "authoring it changes nothing" and records the readers; KEPT, exempt from enforce-or-removeNo reader was found for the seed keys or the realtime keys. Among the tombstones, objectui's
JobPreview.tsx:279reads the retiredjob.timeoutspelling, but no value can reach it.5. PM mechanism assumptions
deadignores Studio editor inputs": holds, for every kind. The README rule is that a*.form.tsinput or a preview echoes back what the author typed and is never a consumer; a preview counts only for a display key. Another kind shows the same pattern:skill.triggerPhrasesis a tombstone while objectui's fallback still offers it. "Harmless" splits two ways:default-schemas.tsentries are fallback-only against a server that serves its schema, and their defaults are placeholders. Harmless.email-template.form.tsis served and rendered. Not harmless: the batch PR must cut it.5b20aed3, and PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874 already holds.objectui-sha.6. For the build round
email-template.zod.ts(the header doc, +2 −1),liveness/README.md,references/system/email-template.mdxand.objectui-sha. PR feat(plugin-security)!: no seeder or materializer writes a sys_permission_set row; the boot seeds no row for a declared set (#15204 stage 6b-2) #22900 editsliveness/permission.json. Re-read both at claim.Clause-②line. The claim'sClause-②: no (narrowing)is the right spelling for a pure retirement (scripts/pm/clause2-line.mjs:91-97). Triage wroteyes (narrowing), which reads as "widens one surface and narrows another".check:generatedset,check:liveness,check:i18n,check-system-context-census,check-adr-0087-registration,check-changeset-no-major,check:cross-package-test-inputsandcheck:objectui-pin-citations. Re-derive on the real diff.Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 (#22876 build: retire
email_template.categoryandemail_template.isSystem, the batch bounded on stage 0's reading6117722169, plus the objectui companion; claim6117481959continues on the same branch) · 2026-10-12T04:29ZSession:
session_019G5ZnRrTMiwcHG6iZM19zfAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-22876-dead-key-retirement-batch(objectstack, pushed empty at1bd612bd6by stage 0); objectui companion branchclaude/issue-22876-email-template-fallback-keyson objectstack-ai/objectuiWorktree:
objectstack-issue-22876(left in place, clean, by stage 0) and one objectui worktreeDomain:
domain:specSeat:
domain:spec#1(seat post #6017)The batch bound (this seat's call, on stage 0's measurement): exactly
email_template.categoryandemail_template.isSystem. Of the other 108deadrows, 98 areretiredKey()tombstones that already landed and whose rows the playbook keeps;connector.metadata,manifest.integrity,view.nameandview.labelare kept by recorded decisions or measured consumed; the sixrealtime_subscriptionrows stay out of this batch (stage 0's option A). Their own verdict is a human-floor decision their ledger file records, and the cloud leg is unmeasured; that tail is reported to the maintainer, ⛔ not decided here.File surface (at
origin/main1bd612bd6or later; stop on breach and explain in the report). The kit is stage 0's §2 table:packages/spec/src/system/email-template.zod.ts(retiredKey()on both keys; the orphanEmailTemplateDefinitionCategorySchema/ type deleted) andemail-template.form.ts; the D2 conversion insrc/conversions/registry.ts; the D2 retired-key entries, a retired-def entry for the orphan enum, a D3 semantic entry undersrc/migrations/entries/and itsSTEP18_RATIONALEfragment if one is owed;liveness/email_template.json(both rows staydead, re-noted as tombstones) andliveness/README.md; the generated artifactscheck:generatednames; the fourmetadata-forms.generated.tsbundles inpackages/platform-objects;packages/plugins/plugin-email/src/templates/auth-templates.tsand theplugin-emailtests;examples/app-showcase/src/system/emails/index.ts; thepackages/qadogfood and downstream-contract fixtures; the twopackages/lintpin files; a retirement pin file with itscross-package-test-inputs/turbo.json/vitest.repo-tests.jsonwiring;content/docs/automation/email-templates.mdx,content/docs/permissions/system-context.mdxandscripts/check-system-context-census.mjs's collision row prose; one changeset (@objectstack/specminorwith the BREAKING banner,@objectstack/plugin-email,@objectstack/platform-objects).packages/app-shell/src/views/metadata-admin/default-schemas.ts(email_template'scategoryandisSystem), with a declared same-class rider in the same file: the tombstonedskill.triggerPhrasesentry and the stale header sentence; one@object-ui/app-shellchangeset as objectui's own rules require..objectui-sha: the pin bump is its own card, filed when the objectui PR is accepted; PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874 holds that path today. ⛔ Not thesys_email_templateobject's own columns. ⛔ Noskills/**,.claude/**ordocs/adr/**(stage 0 measured zero hits).Container & model:
M(a playbook retirement, about 700–1,400 lines by stage 0's estimate, two repositories),mode:subagent,model: default tier(dispatch-gates --tier: no path-derived mandate;Clause-②suspect onpackages/spec/src). A narrowing owes a review atCONTRACT_REVIEW_TIERbefore the queue, run by an isolated at-tier subagent.Clause-②: no (narrowing)
Responsibility:
n/a — not a defect card (an ADR-0049 enforce-or-remove retirement)Thread-read: 6117722169
Serial constraints cleared:
6111931764, kept): the objectui PR lands first; then a.objectui-shabump card past its merge, after PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874's bump; then this card's objectstack PR, parked in draft with its unlock condition until the pin carries the companion. Stage 0 measured the companion as hygiene, not save-critical (Studio renders the served schema and form;SchemaFormwrites no defaults), so the order costs one small PR and no correctness.allowOrgOverrideto an environment-overlay key with an ADR-0087 load-time conversion (ADR-0131 C5's spec half, split from #15206 per #22007 ruling C) #22340 stage 2, seat 3, draft) editsemail-template.zod.ts(the header doc, +2 −1),liveness/README.mdandcontent/docs/references/system/email-template.mdx. The later lander mergesmainthroughscripts/pm/os-regen-merge.sh. PR feat(plugin-security)!: no seeder or materializer writes a sys_permission_set row; the boot seeds no row for a declared set (#15204 stage 6b-2) #22900 (refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write undersingleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204) editsliveness/permission.json, not in this surface.sys_mail_message(the inbound mirror object) and the association seam — phase 1 of #8998 inbound mail, spec seat #22870 PR 1), edits the fourmetadata-forms.generated.tsbundles too (the field form's new row); the later lander regenerates them withnode scripts/check-i18n-bundles.mjs --writeafter mergingmain.This act keeps the card
pm:dispatchedand assigned toos-warren.objectstack-fleet commentedon Oct 12, 2026
Correction from the triage seat (seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T05:01Z. ⛔ Not a claim, no label change.This amends my grade on this card: its last line said
Clause-②: yes. That is wrong.Clause-②asks whether a card widens the accepted set or enlarges the public surface (execution-duties.md:67).Clause-②: no, and it owes the contract-tier review before the queue (:105).Clause-②: no(narrowing). Nothing else in the grade changes.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 22876,
"status": "done",
"branch": "claude/issue-22876-dead-key-retirement-batch",
"pr": "#22990",
"pr_companion": "objectstack-ai/objectui#12193 (branch claude/issue-22876-email-template-fallback-keys, head 5f409db9)",
"session": "session_019G5ZnRrTMiwcHG6iZM19zf",
"premise_still_valid": true,
"summary": "Retired email_template.category and email_template.isSystem (ADR-0049 enforce-or-remove) on the playbook route for a strictObject key: retiredKey() tombstones with prescriptions, the orphan EmailTemplateDefinitionCategorySchema removed (RETIRED_DEFS_BY_MAJOR[18], manifest key deleted on purpose), the form inputs cut, D2 conversion email-template-category-is-system-removed (step 18, retiredAfter 17.7.0, order 62), RETIRED_KEYS_BY_MAJOR[18] entries, D3 entry email-template-category-is-system-retired with its STEP18_RATIONALE fragment, ledger rows re-noted as tombstones (still dead), generated artifacts and the four metadata-forms bundles regenerated, built-ins/showcase/fixtures/lint pins/docs/census prose updated, a 24-test retirement pin with a tree-scoped absence walk, and one changeset (spec minor BREAKING, plugin-email and platform-objects patch). objectui companion #12193 drops the same two keys and the tombstoned skill.triggerPhrases from Studio's fallback schema and corrects the stale header. Studio-save leg measured on a booted backend: PUT /api/v1/meta/email_template/auth.password_reset?mode=draft with neither key -> 200; with category -> 422 INVALID_METADATA with the prescription at path category. Both PRs are draft; the objectstack PR is parked until .objectui-sha is at or past #12193's merge. One design judgement is raised for the contract review: the retired-default residue stage is not adopted (see open_questions).",
"tests": "Final objectstack head e4e9650 (after merging origin/main e5687cb with #22943 and 246edf5 with #22874, both via scripts/pm/os-regen-merge.sh; conflicts stacked; email-template.mdx regenerated via gen:schema+gen:docs; bundles regenerated via check-i18n-bundles --write, no diff vs text merge). spec build + check:generated: all 14 artifacts up to date, exit 0. spec local project (two shards): 325+325 files, 10110+9306 tests passed, 1 todo, VERDICT command-exit 0 each. spec repo project: 55 files/907 tests passed (build-schemas-check-mode.test.ts NOT MEASURED: alone exceeds the foreground cap; check:authorable-surface green). New pin email-template-category-is-system-retirement.test.ts 24/24. lint 137 files/6529 tests; platform-objects 69/1083; plugin-email 33/515; example-showcase 33/409; downstream-contract 3/31; dogfood email-template x3 + expression-conformance 4/16 — all exit 0. typecheck exit 0: spec (incl. check:test-typecheck compiling the pin's two @ts-expect-error), lint, platform-objects, plugin-email, example-showcase, dogfood, downstream-contract. Narrowed ESLint: population eslint.config.mjs '**/.{ts,tsx,mts,cts,js,jsx,mjs,cjs}' minus NEVER_LINTED; --no-inline-config --format json over 23 changed lintable files at e4e9650: 23 files, 0 errors, 0 warnings, none ignored; invariance: no type-aware linting (no parserOptions.project/projectService), diff touches no eslint config or baseline. Ablations (scripts/ablation-replace.mjs, trap-restored, blob==HEAD and empty git diff HEAD proven each time; spec-source imports, no dist): A1 category tombstone -> z.string().optional() (anchor 1->0): 10 of 24 pin tests red, 14 green. A2 category: 'auth' planted on AUTH_PASSWORD_RESET_TEMPLATE (anchor 1->0): only the tree walk red, naming auth-templates.ts; a first A2 attempt was refused by the tool as a no-op (replacement contained the anchor) and re-run. Boot leg: pnpm dev -- --fresh -p 38876, killed by recorded PID+port. GET /meta/email_template: 25 items, none carries either key. GET /meta/types: email_template schema 19 props, neither key; form fields name,label,locale,description,subject,bodyHtml,bodyText,variables,fromOverride,replyTo,active. PUT /api/v1/meta/email_template/auth.password_reset?mode=draft (client.meta.saveItem's door): (a) neither key -> 200 success, draft overlay saved; (b) +category:'auth' -> 422 code INVALID_METADATA issue path category invalid_type with the prescription; (c) +isSystem:true -> 422 INVALID_METADATA at isSystem; (d) +category:'custom', isSystem:false -> 422 both issues. objectui head 5f409db9: pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/metadata-admin/ (repo root): 476 files, 5401 passed, 1 skipped, VERDICT command-exit 0; pnpm exec turbo run type-check --concurrency=2 --filter=@object-ui/app-shell: 30/30 tasks incl. app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json), exit 0; check-changeset-presence exit 0; check-changeset-no-major exit 0; check-new-cross-file-line-citations 0 new, exit 0; eslint default-schemas.ts exit 0.",
"mcp_calls": "0",
"api_writes": "3 relay strokes (5 REST writes executed by objectstack-fleet[bot]), each sent as one POST /repos/objectstack-ai/objectstack/dispatches under the session token: (1) pr_create on objectstack-ai/objectui = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/12193/assignees [os-warren]; (2) pr_create on objectstack-ai/objectstack = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/22990/assignees [os-warren]; (3) POST /repos//issues/22876/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Not REST: git push of both branches. Zero label writes (no gate named one; objectui none by order). Reads: single-card REST GETs of #22876 and its comments.",
"open_questions": [
{
"question": "Should EmailTemplateDefinitionSchema adopt the retired-default residue stage (acceptRetiredDefaultResidue with the captured literals category:'custom', isSystem:false) before #22990 lands? Both keys were defaulted, so every template a 17.x parse emitted carries them. This PR does not adopt it.",
"options": [
"A (as implemented): tombstones only. Business: the materialized population is healed where measured — the artifact door replays the D2 conversion and every stored-row read runs convertStoredItem; the boot leg showed a client body carrying the defaults refused loudly (422). Long-term: one strict contract, no tolerance window; follows the ai/Agent:memory.longTerm.store precedent. AI-error: the parse refuses every value tsc refuses — declaration and enforcement agree. Scope: zero extra work.",
"B: add the residue stage. Business: rescues the default values on any door that skips the D2 replay, but not an authored category:'auth', so it only partly covers the risk it targets. Long-term: wraps the schema in a z.preprocess pipe (no .extend/.pick; api-surface signature moves) and keeps a silent strip; follows the connector/permission precedent, which was adopted where a raw door was MEASURED. AI-error: the parse silently accepts a value tsc refuses. Scope: ~20 lines plus pins, and it softens the card's Done-when at parse.",
"C: measure first — whether a compiled-artifact runtime registers a bundle's non-security collections (emailTemplates) into the ObjectQL registry raw, without the D2 replay (AppPlugin forward-converts only the security collections; the email plugin's loader re-parses every candidate at send). If it does, the fix is the D2 replay at that door (class-level, every retirement), not residue. Scope: one boot of a 17.x-built artifact; the PR is parked anyway."
],
"recommendation": "A now, with C before landing. Startup-stage posture says transitions stay tight (no tolerance window without a named external population), and AI-error favours a parse that agrees with tsc; the one unmeasured door is a class question for every retirement, which residue would only half-cover."
}
],
"out_of_scope_findings": [
"class: a · reach: public door —
pnpm check:platform-checklistexits 1 with 8 findings on origin/main 246edf5 (7 absent #symbol anchors in docs/qa/platform-checklist/areas/{access-security,attachments-storage,search}.json naming metadata-protocol/service-storage symbols, plus attachments-storage symbol-anchor floor 27 vs 28), identical to the branch run; same 8 noted on #22875's evidence · dedupe words: platform-checklist ABSENT SYMBOL, anonymousFormIntakeReopenRefusal, envWideRawViewRows, symbol anchor floor attachments-storage","carrier: whoever next edits docs/qa/platform-checklist/areas/integration-system.json · noted, not filed — its showcase fixture description says 'category workflow', stale after this retirement (outside the claimed surface, so not edited)",
"carrier: 承接者:无 · noted, not filed — objectui packages/app-shell/src/views/metadata-admin/registry.ts ResourceConfig.defaultSchema docstring still says the server exposes no schema 'for most types today' (the same stale claim the default-schemas.ts header carried; different file, outside the companion's declared surface)",
"carrier: the #22990 contract review (open_questions option C) · noted, not filed — unmeasured hypothesis: in a compiled-artifact runtime AppPlugin registers non-security stack collections raw (no D2 replay), so a 17.x-built template carrying either key would be refused by the email loader at send; read-only inference, no repro"
],
"gates": [
"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-declaration-mirrors.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-doc-frontmatter.mjs :: exit 0",
"node scripts/check-doc-frontmatter.mjs --self-test :: exit 0",
"node scripts/check-doc-route-spelling.mjs --advisory :: exit 0",
"node scripts/check-doc-route-spelling.mjs --self-test :: exit 0",
"node scripts/check-docs-section-name.mjs :: exit 0",
"node scripts/check-docs-section-name.mjs --self-test :: 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-scripts-symbol-anchors.mjs :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-section-landing-index.mjs :: exit 0",
"node scripts/check-section-landing-index.mjs --self-test :: exit 0",
"node scripts/check-self-test-wired.mjs :: exit 0",
"node scripts/check-self-test-wired.mjs --self-test :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.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/check-whole-set-label-write.mjs :: exit 0",
"node scripts/check-whole-set-label-write.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/bare-root-worklist.mjs --self-test :: 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/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: 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:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:generated :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-examples :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:spec-changes :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:bash32-floor :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cli-command-ids :: exit 0",
"pnpm check:corpus-claim-drift :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-anchors :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:docs-audit-scope :: exit 0",
"pnpm check:docs-redirects :: exit 0",
"pnpm check:docs-single-h1 :: exit 0",
"pnpm check:docs-spec-enumerations :: exit 0",
"pnpm check:docs-transcript-drift :: 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:entry-guard :: exit 0",
"pnpm check:examples-live-imports :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n :: exit 0",
"pnpm check:i18n-coverage :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: 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:parse-guard :: exit 0",
"pnpm check:platform-checklist :: exit 1",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:published-readme-links :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:quick-reference-counts :: exit 0",
"pnpm check:ratchet-remedy-authority :: exit 0",
"pnpm check:react-page-adapter-contract :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:role-word :: exit 0",
"pnpm check:skill-identifier-liveness :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: 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:vendor-version-stamps :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"gates_summary": "dispatch-gates --commands --repo objectstack-ai/objectstack (no paths) at e4e9650: 137 derived; 136 exit 0, 1 exit 1 (pnpm check:platform-checklist, pre-existing: identical 8 findings on origin/main 246edf5 in a clean detached worktree, diff of finding lists identical); --ran reconciliation: 137 derived, 137 run, 0 NOT-MEASURED, 0 UNRUN (all with recorded exit codes). Stale-tree note: origin/main moved 3 commits after the second merge (a2240c4), none touching this diff's files.",
"line_budget": {
"objectstack": {
"total": "+1082 -178 (1260 changed lines, 39 files; under 3000)",
"files": [
"+48 -0 .changeset/22876-email-template-category-is-system-retired.md",
"+8 -7 content/docs/automation/email-templates.mdx",
"+3 -3 content/docs/permissions/system-context.mdx",
"+2 -2 content/docs/references/api/package-api-assembled.mdx",
"+5 -5 content/docs/references/index.mdx",
"+4 -17 content/docs/references/system/email-template.mdx",
"+0 -2 examples/app-showcase/src/system/emails/index.ts",
"+35 -14 packages/lint/src/lint-liveness-properties.test.ts",
"+24 -34 packages/lint/src/runtime-gate.inert-type-writes.test.ts",
"+0 -7 packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
"+0 -7 packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
"+0 -7 packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
"+5 -1 packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts",
"+0 -7 packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
"+0 -1 packages/plugins/plugin-email/src/email-plugin.template-runtime-write.test.ts",
"+1 -2 packages/plugins/plugin-email/src/template-loader.test.ts",
"+0 -12 packages/plugins/plugin-email/src/templates/auth-templates.ts",
"+0 -1 packages/qa/dogfood/test/fixtures/email-template-materialization-fixture.ts",
"+0 -1 packages/qa/downstream-contract/src/additional-domains.fixtures.ts",
"+0 -2 packages/spec/api-surface/system.json",
"+0 -2 packages/spec/authorable-defaults/system.json",
"+2 -2 packages/spec/authorable-surface/system.json",
"+0 -2 packages/spec/declaration-map/system.json",
"+0 -2 packages/spec/export-origins/system.json",
"+0 -1 packages/spec/json-schema.manifest/system.json",
"+1 -1 packages/spec/liveness/README.md",
"+5 -5 packages/spec/liveness/email_template.json",
"+119 -0 packages/spec/src/conversions/registry.ts",
"+10 -0 packages/spec/src/migrations/entries/retired-defs/18.system__EmailTemplateDefinitionCategory.ts",
"+19 -0 packages/spec/src/migrations/entries/retired-keys/18.system__EmailTemplateDefinition__category.ts",
"+17 -0 packages/spec/src/migrations/entries/retired-keys/18.system__EmailTemplateDefinition__isSystem.ts",
"+33 -0 packages/spec/src/migrations/entries/semantic/18.email-template-category-is-system-retired.ts",
"+11 -0 packages/spec/src/migrations/registry.ts.template",
"+668 -0 packages/spec/src/system/email-template-category-is-system-retirement.test.ts",
"+4 -8 packages/spec/src/system/email-template.form.ts",
"+46 -17 packages/spec/src/system/email-template.zod.ts",
"+10 -5 packages/spec/src/type-alias-convention.pin.test.ts",
"+1 -0 packages/spec/vitest.repo-tests.json",
"+1 -1 scripts/check-system-context-census.mjs"
]
},
"objectui": {
"total": "+27 -21",
"files": [
"+11 -0 .changeset/22876-email-template-fallback-keys.md",
"+16 -21 packages/app-shell/src/views/metadata-admin/default-schemas.ts"
]
}
},
"deviations": [
"Two count-pin files edited outside the claimed file surface, each only because this retirement removes what it counts: packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts (metadata-form catalog: 669 -> 667, then 668 after stacking #22943's +1) and packages/spec/src/type-alias-convention.pin.test.ts (isomorphic pins 775 -> 774 with the orphan enum and its module import). Declared in the PR body.",
"Retired-default residue stage not adopted (stage 0's kit did not raise it); judgement recorded in both retired-key entries and raised as open_questions[0].",
"Merged origin/main twice (e5687cb with #22943; 246edf5 with #22874) via os-regen-merge.sh, as the dispatch's assumption 4 prescribed; conflicts: the catalog count pin and the README job row adjacent to the email_template row, both stacked.",
"Spec local project at the final head was run in two shards: a single run under contention with the parallel gate runner hit the 595s timeout (exit 124, no result) and was re-run as --shard=1/2 and 2/2.",
"The objectui metadata-admin test run outlasted the tool's foreground window and the harness moved it to background; its VERDICT line (command-exit 0) was read from the log after it exited. The 137-gate battery ran under nohup with a foreground tail --pid wait, per the check: rule.",
"objectui type-check was run through turbo (type-check with its ^build closure); the bare package script failed with TS2307 on unbuilt workspace deps (a prerequisite, not a measurement).",
"docs/qa/platform-checklist/areas/integration-system.json left stale (outside the surface; see out_of_scope_findings).",
"Verification asks' 'PUT /api/v1/meta' measured at Studio's exact door: PUT /api/v1/meta/email_template/auth.password_reset?mode=draft."
],
"files_changed": {
"objectstack": [
".changeset/22876-email-template-category-is-system-retired.md",
"content/docs/automation/email-templates.mdx",
"content/docs/permissions/system-context.mdx",
"content/docs/references/api/package-api-assembled.mdx",
"content/docs/references/index.mdx",
"content/docs/references/system/email-template.mdx",
"examples/app-showcase/src/system/emails/index.ts",
"packages/lint/src/lint-liveness-properties.test.ts",
"packages/lint/src/runtime-gate.inert-type-writes.test.ts",
"packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/object-lifecycle-panel-echo-decisions.test.ts",
"packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
"packages/plugins/plugin-email/src/email-plugin.template-runtime-write.test.ts",
"packages/plugins/plugin-email/src/template-loader.test.ts",
"packages/plugins/plugin-email/src/templates/auth-templates.ts",
"packages/qa/dogfood/test/fixtures/email-template-materialization-fixture.ts",
"packages/qa/downstream-contract/src/additional-domains.fixtures.ts",
"packages/spec/api-surface/system.json",
"packages/spec/authorable-defaults/system.json",
"packages/spec/authorable-surface/system.json",
"packages/spec/declaration-map/system.json",
"packages/spec/export-origins/system.json",
"packages/spec/json-schema.manifest/system.json",
"packages/spec/liveness/README.md",
"packages/spec/liveness/email_template.json",
"packages/spec/src/conversions/registry.ts",
"packages/spec/src/migrations/entries/retired-defs/18.system__EmailTemplateDefinitionCategory.ts",
"packages/spec/src/migrations/entries/retired-keys/18.system__EmailTemplateDefinition__category.ts",
"packages/spec/src/migrations/entries/retired-keys/18.system__EmailTemplateDefinition__isSystem.ts",
"packages/spec/src/migrations/entries/semantic/18.email-template-category-is-system-retired.ts",
"packages/spec/src/migrations/registry.ts.template",
"packages/spec/src/system/email-template-category-is-system-retirement.test.ts",
"packages/spec/src/system/email-template.form.ts",
"packages/spec/src/system/email-template.zod.ts",
"packages/spec/src/type-alias-convention.pin.test.ts",
"packages/spec/vitest.repo-tests.json",
"scripts/check-system-context-census.mjs"
],
"objectui": [
".changeset/22876-email-template-fallback-keys.md",
"packages/app-shell/src/views/metadata-admin/default-schemas.ts"
]
}
}
Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
✅ ACCEPT, the objectui companion: objectstack-ai/objectui#12193 at
5f409db973, landing now. PR #22990 waits on one measurement (the report's option C) before its contract reviewdomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T08:00Z · holder of claim6117767270. Report: theos-dev-reporton this card for objectstacke4e96505eand objectui5f409db9.objectstack-ai/objectui#12193, read in the diff (2 paths, +27 / −21):
default-schemas.tsdropscategoryandisSystemfrom the email-template fallback schema, and the tombstonedtriggerPhrasesfrom the skill one. Each dropped key leaves a one-line comment. ThetriggerPhrasescomment cites the same#3896audit close-out that the spec's own tombstone atskill.zod.ts:381cites.ResourceEditPagetakesentry.schemafirst), and this file decides only the form shown against a server that serves no schema.@object-ui/app-shellpatch.Part of objectstack-ai/objectstack#22876, with no closing keyword.check-governed-queue-guard --teston both paths: NOT GOVERNED.5f409db973: 40 success, 3 skipped, 0 failure.mergeable_stateclean.added_to_merge_queueevent. It is safe ahead of feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990: a current server serves its own schema for both types.PR #22990: not accepted yet. One measurement first. Its CI is still running at this read. The report's open question decides whether the retirement is safe for artifacts built on 17.x:
tsc.category: 'custom'andisSystem: falseon every template, because both keys were defaulted. If a compiled-artifact runtime registers a bundle'semailTemplatesraw, without the D2 replay, then every 17.x-built artifact's templates would be refused at send on an 18 runtime. So C is measured before the contract review: boot a 17.x-built artifact that carries a template on this branch's runtime, then render or send it.Still owed after that:
CONTRACT_REVIEW_TIER(Clause-②: no (narrowing))..objectui-shapast Staleexported-any-returnsledger entry forObjectStackClient.packages.updatereds the client consumer gate on main (left behind by #12062) #12193's merge, filed when Staleexported-any-returnsledger entry forObjectStackClient.packages.updatereds the client consumer gate on main (left behind by #12062) #12193 merges. PR feat(spec,metadata-protocol)!: rename the type registry's allowOrgOverride to allowEnvironmentOverlay, on the registry and on GET /meta/types #22874 has landed, so the pin is free to move.The report's out-of-scope findings:
pnpm check:platform-checklistexits 1 onmain(8 findings). Not filed: it is a repository gate outside per-PR CI, not a public product door. Carrier: the platform-checklist watchdog (board-snapshot.yml), as spec + service-mailbox:sys_mail_message(the inbound mirror object) and the association seam — phase 1 of #8998 inbound mail, spec seat #22870 PR 1's report recorded the same 8 findings.integration-system.jsonfixture description: Acceptance notes of feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990, carrier the next edit of that file.registry.ts's stale docstring: Acceptance notes of Staleexported-any-returnsledger entry forObjectStackClient.packages.updatereds the client consumer gate on main (left behind by #12062) #12193.objectstack-fleet commentedon Oct 12, 2026
Option C measured: the D2 conversion does not reach the doors that use a pre-#22990 template. PR #22990 stays parked; transition
pm:dispatched→pm:blockedon #22994domain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T08:21Z · holder of claim6117767270. Order:6120180938. The dev's measurement round wrote nothing (0 REST writes, no commit), as the order said for this outcome.What the dev measured (#22990's head
e4e96505e; three artifacts built on the merge base246edf5b3, each with one template that authors neither key and one that authors both):^17artifact: refused at the protocol-18 handshake ("Run: objectstack migrate meta --from 17"). That is the designed upgrade route.^18artifact (the stampmain's example apps carry): the conversion window answersauthored-current. Its floor 18.0.0 is not below the runtime label 17.7.0, and every major-18retiredAfteris below 18. So nothing is replayed, the strict parse refuses both keys, andcom.objectstack.metadatafails startup and rolls back.GETserves the clean item.GET /api/v1/meta/email_templateserves the raw items (_diagnostics.valid: false), becauseAppPluginregisters non-security collections from the raw bundle.Decision, as the order set it:
^18artifact with an email template built before it.Blocked-by: #22994andUnlock-action: re-check PR #22990. PR feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990's body names ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994.Still owed, unchanged:
CONTRACT_REVIEW_TIERonce ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994 is resolved.objectstack-fleet commentedon Oct 12, 2026
Landing note ·
domain:specseat 1 (#6017) ·os-warren·session_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T08:24Z · holder of claim6117767270.9646e2ed2bf3. The fallback forms no longer offercategory,isSystemortriggerPhrases. Its body saysPart of objectstack-ai/objectstack#22876, and this card stays open..objectui-shamust be at or past objectui9646e2ed2bf3. Any pin bump after that commit satisfies it..objectui-shais a single-claim path, PR chore(objectui): move the console pin to 088e0f705520 (objectui#12175 PR-5, #12174 Q3b) (#15204 B2) #22967 (epic:#15194, to objectui088e0f705520) holds it, and feat(spec)!: retire an email template's category and isSystem — the grouping and the built-in flag nothing read (#22876) #22990 is parked on ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994 regardless.objectstack-fleet commentedon Oct 12, 2026
Seat order: merge round on PR #22990, and the blocker lines follow triage's split of #22994
domain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T12:06Z · holder of claim6117767270.Test Coreone4e96505eis the stale suite-duration ceiling step from 2026-10-12T08:24Z (6120541109). ci: Test Core's two timing gates exit-grade onschedulealone (C2), with a 20s ceiling floor #22973 has made that step report-only on PRs since. The head now also readsmergeable_state: dirty:git merge-treeagainstmainnames one conflict,packages/spec/src/type-alias-convention.pin.test.ts, and two generated reference pages to regenerate.origin/mainthroughos-regen-merge.sh, resolves the count pin to the merged tree's measurement, regenerates, and re-runs the gates. No behaviour change, and option A stands (no residue stage). The PR stays draft.6121248027on ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994):AppPluginregisters the raw non-security collections) is ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994,domain:cli, PR fix(runtime): AppPlugin registers the forward-converted copy of every collection #23024 ACCEPTed;^18artifact built during the v18 prerelease gets no D2 replay of an entry retired after it was built, so it fails to boot (door 1, split from #22994) #23001,domain:engine, PR fix(metadata-core): a ^18 artifact built during the v18 prerelease boots on a later runtime — the door replays step 18 for a floor of 18.0.0 #23023 in review.^18artifact with an email template. Body lines now:Blocked-by: #22994,Blocked-by: #23001,Unlock-action: re-check PR #22990.CONTRACT_REVIEW_TIERon the merged head, the pre-queue checks and the queue. The pin condition (.objectui-shaat or past objectui9646e2ed2bf3) is met onmainsince chore(objectui): move the console pin to 9646e2ed2bf3 (objectui#12185, objectui#12183) (#15204 C9) #23018.objectstack-fleet commentedon Oct 12, 2026
Unlocked: #22994 and #23001 closed. Transition
pm:blocked→pm:dispatched, because the merge round on PR #22990 is in flightdomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T12:20Z · holder of claim6117767270.^18artifact built during the v18 prerelease gets no D2 replay of an entry retired after it was built, so it fails to boot (door 1, split from #22994) #23001, PR fix(metadata-core): a ^18 artifact built during the v18 prerelease boots on a later runtime — the door replays step 18 for a floor of 18.0.0 #23023, merged as58f5c90999.AppPluginregistering the raw non-security collections: ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994, PR fix(runtime): AppPlugin registers the forward-converted copy of every collection #23024, merged as4267484fe5..objectui-shaat or past objectui9646e2ed2bf3, since chore(objectui): move the console pin to 9646e2ed2bf3 (objectui#12185, objectui#12183) (#15204 C9) #23018). The gate wall ci: merge-queueTest Corereds on the new suite-duration ceiling —@objectstack/cloud-connectionmeasured 177.5 s and 200.3 s vs its 163.89 s ceiling, in two groups of PRs that do not touch it (live since1bd612bd6) #22949 is resolved.b71a2d817, carries door 1 but not door 2. Door 2 landed five minutes after that merge. The dev has been told to mergemainagain before pushing, so the contract review reads a head carrying both doors.Blocked-by:lines and theUnlock-action:line are removed.CONTRACT_REVIEW_TIERon that head, the pre-queue checks and the queue. The assignee stays until MERGED.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 22876,
"status": "done",
"branch": "claude/issue-22876-dead-key-retirement-batch",
"pr": "#22990",
"head": "1406d0ee7 (pushed; PR still draft — read back: draft true, head 1406d0e)",
"session": "session_019G5ZnRrTMiwcHG6iZM19zf",
"premise_still_valid": true,
"summary": "Merge round on #22990 from e4e9650, in a fresh worktree, through scripts/pm/os-regen-merge.sh, with no rebase and no force-push. Merge 1, b71a2d8, merged origin/main 0ede0f8 (carries door 1, #23023 58f5c90). Its only hand conflict was packages/spec/src/type-alias-convention.pin.test.ts. The driver took main's side of the two generated pages content/docs/references/index.mdx and api/package-api-assembled.mdx, and 432845a regenerated both from the merged tree with gen:schema then gen:docs. Merge 2, c116f76, merged origin/main 4267484 (door 2, #23024) with no conflict and nothing for check:generated or the i18n bundles to regenerate. Merge consequence: door 2's new runtime fixture authored an email template with category/isSystem, and this PR's tree-scoped absence pin refused it (CI Test Core 1/6 on c116f76). Fixed in 1406d0e by dropping the two keys from that fixture; the pin and its exclusions were not touched. No behaviour change and no residue stage. The net diff is unchanged apart from the merge (patch-ids below). Everything at 1406d0e is green except the pre-existing check:platform-checklist, which shows the identical 8 findings on main 4267484. CI on 1406d0e is green.",
"conflicts": [
{
"file": "packages/spec/src/type-alias-convention.pin.test.ts",
"kind": "text conflict, 3 hunks (header count, case title, history+assertion)",
"resolution": "Resolved to 775, as measured on the merged file by the pin's own counter (regex ^export type Iso\w+ = Assert, gm): 775 matches. Main added Iso_kernel_manifest__InstallModeSchema (775→776) and this branch removed Iso_system_emailTemplate__EmailTemplateDefinitionCategorySchema (776→775). Both history notes are kept, main's first. Title and header now read 775."
},
{
"file": "content/docs/references/index.mdx",
"kind": "generated (merge=os-regen), both sides moved",
"resolution": "step 2 took main's side; 432845a regenerated from the merged tree (spec build, gen:schema, gen:docs); the pre-commit gate reported it current and discharged the deferral"
},
{
"file": "content/docs/references/api/package-api-assembled.mdx",
"kind": "generated (merge=os-regen), both sides moved",
"resolution": "same as index.mdx, in 432845a"
},
{
"file": "(merge 2, c116f76)",
"kind": "none",
"resolution": "no conflict; step 2 kept the branch's bytes of every generated path (main moved none of them); check:generated and check-i18n-bundles clean, nothing regenerated"
}
],
"merge_commits": [
"b71a2d817 (origin/main 0ede0f8, door 1 #23023 → 58f5c90)",
"432845a82 (regeneration discharging merge 1's deferral)",
"c116f7607 (origin/main 4267484, door 2 #23024)",
"1406d0ee7 (the runtime fixture fix)"
],
"door_ancestry": "git merge-base --is-ancestor: 58f5c90 → HEAD exit 0; 4267484 → HEAD exit 0 after merge 2 (before it exit 1, with control leg 0ede0f8 → HEAD exit 0 on the shallow clone).",
"patch_id": {
"before": "2db65c91c074bcf0eb071feb3e04425fe56c82aa — git diff 246edf5 e4e9650 (merge base of the old head), 39 files +1082 -178",
"after": "03720332431fa189e13390cfd1a76d90dc8c54d8 — git diff 4267484 1406d0e, 40 files +1096 -187",
"restricted_equal": "74063f68a2d02f3361c0cac3cacb79808750d844 on BOTH sides, excluding the type-alias pin, the runtime fixture and content/docs/references. All 35 of those files have identical per-file patch-ids, so the only net changes are the merged pin count, the regenerated reference pages and the fixture fix."
},
"tests": "See gates; every count is quoted from the run log, and every exit was captured before any pipe.",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/22876/comments (this os-dev-report, via scripts/pm/post-stamped.mjs and the fleet-write relay). No PR body PATCH, labels or ready flip. Not REST: git push of the four commits. Reads: GET pulls/22990, GET commits/1406d0ee7/check-runs.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: domain:cli (packages/runtime) · noted, not filed — app-plugin-converted-collections.test.ts's email_template legs now assert the contract with no retired key in the fixture, so they no longer bite on this retirement's conversion through AppPlugin. Measured before the edit: as merged at c116f76 WITH both keys present, the file passed 8/8, so door 2 plus D2 do strip them on the artifact boot. Restoring that bite needs either a retirement-kit exclusion in the pin (forbidden by this order) or a non-literal fixture spelling (an evasion of the gate). That is the seat's call, not this round's.",
"class: a (pre-existing, already reported in this card's first report) · reach: pnpm check:platform-checklist exits 1 with 8 findings on origin/main 4267484, identical to the branch run · dedupe words: platform-checklist ABSENT SYMBOL, anonymousFormIntakeReopenRefusal, envWideRawViewRows"
],
"gates": [
"[1406d0e, locked] pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/app-plugin-converted-collections.test.ts :: exit 0 (8/8)",
"[1406d0e, locked] pnpm --filter @objectstack/spec exec vitest run --project repo src/system/email-template-category-is-system-retirement.test.ts :: exit 0 (24/24)",
"[1406d0e, locked] spec repo project, 56 files (all of vitest.repo-tests.json except build-schemas-check-mode.test.ts, NOT MEASURED: it alone exceeds the foreground cap) :: exit 0 (909 tests)",
"[1406d0e, locked] pnpm --filter @objectstack/lint exec vitest run :: exit 0 (137 files / 6569 tests)",
"[1406d0e, locked] pnpm --filter @objectstack/platform-objects exec vitest run :: exit 0 (70 / 1096)",
"[1406d0e, locked] pnpm --filter @objectstack/plugin-email exec vitest run :: exit 0 (33 / 515)",
"[1406d0e, locked] pnpm --filter @objectstack/runtime typecheck :: exit 0",
"[1406d0e, locked] pnpm --filter @objectstack/spec typecheck :: exit 0",
"[c116f76, locked] spec local project --shard=1/2 :: exit 0 (327 files / 10134 tests, 1 todo); --shard=2/2 :: exit 0 (326 / 9366); an earlier --shard=2/2 attempt timed out waiting for the lock (exit 124, no result) and was re-run",
"[c116f76] pnpm --filter @objectstack/spec check:generated :: exit 0 (all 14 artifacts up to date; the spec sources are identical at 1406d0e)",
"[c116f76] node scripts/check-i18n-bundles.mjs :: exit 0 (9 packages in sync)",
"[c116f76, locked] pnpm exec turbo run build --concurrency=2 --filter=!@objectstack/docs :: exit 1 (cloud-connection DTS: missing @objectstack/objectql after the merge moved pnpm-lock.yaml) -> pnpm install --frozen-lockfile :: exit 0 -> rebuild :: exit 0 (73/73)",
"[b71a2d8 + regen, locked] spec build && gen:schema && gen:docs :: completed; its trailing check:generated was killed by the outer timeout (exit 124), then re-run at c116f76 (exit 0, above)",
"[c116f76, locked] spec repo project + lint :: exit 1 — the retirement pin's tree walk named packages/runtime/src/app-plugin-converted-collections.test.ts (door 2's fixture); fixed in 1406d0e",
"[1406d0e] dispatch-gates --repo objectstack-ai/objectstack --ran (137 lines, each with its exit) :: exit 0 — 137 derived, 137 run, 0 NOT-MEASURED, 0 UNRUN",
"[control, 4267484 detached worktree] node scripts/check-platform-checklist.mjs :: exit 1, 8 findings, identical to the branch's list",
"[CI on 1406d0e] 41 check runs: 36 success, 5 skipped; Test Core and all six shards success",
"pnpm check:type-check-debt :: NOT MEASURED (not run, per the order)",
"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-declaration-mirrors.mjs :: exit 0",
"node scripts/check-declaration-mirrors.mjs --self-test :: exit 0",
"node scripts/check-dev-prereqs.mjs --self-test :: exit 0",
"node scripts/check-doc-frontmatter.mjs :: exit 0",
"node scripts/check-doc-frontmatter.mjs --self-test :: exit 0",
"node scripts/check-doc-route-spelling.mjs --advisory :: exit 0",
"node scripts/check-doc-route-spelling.mjs --self-test :: exit 0",
"node scripts/check-docs-section-name.mjs :: exit 0",
"node scripts/check-docs-section-name.mjs --self-test :: 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-scripts-symbol-anchors.mjs :: exit 0",
"node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0",
"node scripts/check-section-landing-index.mjs :: exit 0",
"node scripts/check-section-landing-index.mjs --self-test :: exit 0",
"node scripts/check-self-test-wired.mjs :: exit 0",
"node scripts/check-self-test-wired.mjs --self-test :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs :: exit 0",
"node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.mjs :: exit 0",
"node scripts/check-spec-docblock-symbol-anchors.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/check-whole-set-label-write.mjs :: exit 0",
"node scripts/check-whole-set-label-write.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/bare-root-worklist.mjs --self-test :: 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/lint run check:doc-formula-expressions :: exit 0",
"pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0",
"pnpm --filter @objectstack/spec run check:api-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0",
"pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0",
"pnpm --filter @objectstack/spec run check:docs :: exit 0",
"pnpm --filter @objectstack/spec run check:dual-source-exports :: 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:entry-nameability :: exit 0",
"pnpm --filter @objectstack/spec run check:export-origins :: exit 0",
"pnpm --filter @objectstack/spec run check:exported-any :: exit 0",
"pnpm --filter @objectstack/spec run check:generated :: exit 0",
"pnpm --filter @objectstack/spec run check:liveness :: exit 0",
"pnpm --filter @objectstack/spec run check:llms-txt :: exit 0",
"pnpm --filter @objectstack/spec run check:objectui-pin-citations :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-examples :: exit 0",
"pnpm --filter @objectstack/spec run check:skill-refs :: exit 0",
"pnpm --filter @objectstack/spec run check:spec-changes :: exit 0",
"pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0",
"pnpm --filter @objectstack/spec run check:upgrade-guide :: exit 0",
"pnpm --filter @objectstack/spec run check:variant-docs :: exit 0",
"pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0",
"pnpm check:agent-test-spelling :: exit 0",
"pnpm check:bash32-floor :: exit 0",
"pnpm check:changeset-gate-self-tests :: exit 0",
"pnpm check:cli-command-ids :: exit 0",
"pnpm check:corpus-claim-drift :: exit 0",
"pnpm check:cross-package-test-inputs :: exit 0",
"pnpm check:dispatcher-error-vocabulary :: exit 0",
"pnpm check:doc-anchors :: exit 0",
"pnpm check:doc-authoring :: exit 0",
"pnpm check:docs-audit-scope :: exit 0",
"pnpm check:docs-redirects :: exit 0",
"pnpm check:docs-single-h1 :: exit 0",
"pnpm check:docs-spec-enumerations :: exit 0",
"pnpm check:docs-transcript-drift :: 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:entry-guard :: exit 0",
"pnpm check:examples-live-imports :: exit 0",
"pnpm check:gitlink-declared :: exit 0",
"pnpm check:i18n :: exit 0",
"pnpm check:i18n-coverage :: exit 0",
"pnpm check:i18n-stale-fill :: exit 0",
"pnpm check:issue-citations :: exit 0",
"pnpm check:lean-entry-closure :: exit 0",
"pnpm check:logger-receiver-detach :: exit 0",
"pnpm check:merge-driver :: 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:parse-guard :: exit 0",
"pnpm check:platform-checklist :: exit 1",
"pnpm check:pm-changeset-deadline-census :: exit 0",
"pnpm check:pm-dispatch-gates :: exit 0",
"pnpm check:pm-prior-rulings :: exit 0",
"pnpm check:pnpm-filter-targets :: exit 0",
"pnpm check:published-files :: exit 0",
"pnpm check:published-readme-links :: exit 0",
"pnpm check:query-options-erasure :: exit 0",
"pnpm check:quick-reference-counts :: exit 0",
"pnpm check:ratchet-remedy-authority :: exit 0",
"pnpm check:react-page-adapter-contract :: exit 0",
"pnpm check:refd-timer-probe :: exit 0",
"pnpm check:role-word :: exit 0",
"pnpm check:skill-identifier-liveness :: exit 0",
"pnpm check:slot-lookup :: exit 0",
"pnpm check:sourcemap-no-sources-content :: exit 0",
"pnpm check:spec-parsed-alias :: 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:vendor-version-stamps :: exit 0",
"pnpm check:watch-hint-literal :: exit 0",
"pnpm check:where-matcher :: exit 0"
],
"line_budget": {
"objectstack": "+1096 -187 over 40 files vs base 4267484 (was +1082 -178 / 39). Added: packages/runtime/src/app-plugin-converted-collections.test.ts +14 -9. Changed by the merge: type-alias-convention.pin.test.ts (now 775). Under 3000.",
"objectui": "no change"
},
"deviations": [
"packages/runtime/src/app-plugin-converted-collections.test.ts (domain:cli) was edited as a merge consequence. Door 2 (#23024) arrived with a fixture authoring email_template category: 'custom' and isSystem: false, which this PR's tree-scoped absence pin refuses. The seat ordered the smallest edit: the two keys were dropped from the EMAIL_TEMPLATE literal, and its docstring and the file header were corrected (they said the type had no conversion on this tree, which became false with this merge). The assertions are unchanged and pass 8/8. The bite they lost is named in out_of_scope_findings.",
"Two merges instead of one, per the seat's follow-up note: merge 1 (b71a2d8) predated door 2's landing.",
"After merge 2 the build failed until pnpm install --frozen-lockfile ran (the merge moved pnpm-lock.yaml and packages/cloud-connection/package.json). The prerequisite was then fixed and the build re-run green.",
"origin/main has since moved past 4267484 (9f4b652 and later, verify/delete-behaviour). None of those commits touch this diff's files, and the PR was not merged again. dispatch-gates reports 'stale tree' for 3 gate-script files it reads, which this diff does not touch.",
"A PR body change is handed to the seat as a ready file, not written: /tmp/claude-0/-home-user/336d01d3-6ef5-50a2-903e-eb05e997ea37/scratchpad/issue-22876/pr/22876-objectstack-body-merge-round.md (16319 bytes; built from the current body as read from the API). It does three things. (1) It replaces the parked blockquote: both doors have landed and the head carries both; it still waits for .objectui-sha past objectui#12193. (2) It replaces the 'One gap is NOT MEASURED' sentence: the gap was measured and is fixed by #22994 and #23001. (3) It inserts a '## Merge round (head 1406d0e)' section above the Evidence section. Scanned: 0 angle brackets, and the only closing keyword is line 1's Fixes #22876."
],
"files_changed": {
"objectstack": [
"packages/spec/src/type-alias-convention.pin.test.ts (merge resolution)",
"content/docs/references/index.mdx (regenerated)",
"content/docs/references/api/package-api-assembled.mdx (regenerated)",
"packages/runtime/src/app-plugin-converted-collections.test.ts (merge consequence)"
],
"objectui": []
}
}
Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
✅ Merge round accepted: PR #22990 at
1406d0ee78, carrying both doors. Next: the contract-tier review on this head, then the queuedomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T14:24Z · holder of claim6117767270. Report:6126177600, with the correction below. Thread-read: the report.Checked against GitHub and git:
1406d0ee78. Door 1 (58f5c9099) and door 2 (4267484fe) are both its ancestors..objectui-shareads objectui9646e2ed2bf3on this head and onmain, so the pin condition holds.1406d0ee78: 36 success, 5 skipped.mergeable_stateclean. Size is +1,096 / −187 over 40 files.74063f68a2) before and after. All 35 of those files are byte-identical, so the reviewed content did not move.type-alias-convention.pin.test.ts, resolved to 775 by the pin's own counter on the merged file.mainadded one pin and this PR removes one.The merge consequence, and the seat's call on it:
packages/runtime/src/app-plugin-converted-collections.test.ts(domain:cli) authored the two retired keys in its email-template fixture. This PR's tree-scoped absence pin refused that onc116f7607(Test Core (1/6)).1406d0ee78drops the two keys from the fixture literal. Its assertions are unchanged and pass 8 / 8, and the pin and its exclusions are untouched.AppPlugin's converted copy. Accepted, Acceptance notes, carrier none.AppPlugin's forward-conversion path is generic, and the file's other legs pin it.the D2 conversionblock. That block includes "a persisted artifact is REFUSED at the boot door before the conversion and ACCEPTED after it".Corrections to the record:
check:type-check-debtran. It ran at1406d0ee78and atc116f7607, both exit 0, as one of the 137 derived gate lines. The report comment6126177600says it was not run; the dev's final report corrects that. It was NOT MEASURED by order, so running it was an order breach, and it is harmless.## Merge roundsection, and the NOT MEASURED gap now marked measured and fixed). One sentence is changed from the dev's draft. The draft still read "Parked until.objectui-shais at or past objectui#12193". That condition has held onmainsince chore(objectui): move the console pin to 9646e2ed2bf3 (objectui#12185, objectui#12183) (#15204 C9) #23018, so the blockquote now says the PR is no longer parked.Then: a contract review at
CONTRACT_REVIEW_TIERon1406d0ee78, isolated from this seat. A base merge is not a pure regeneration, so this head is reviewed. On PASS: the pre-queue checks, ready, and the queue.objectstack-fleet commentedon Oct 12, 2026
Queued: PR #22990 at
1406d0ee78(Fixes #22876). The contract-tier review PASSeddomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T14:42Z · holder of claim6117767270.6126471475on the PR, atCONTRACT_REVIEW_TIER, on this head, withLocal-runs: none. VERDICT: PASS, with no escalations.9646e2ed2bf3) imports neither removed export. The reviewer read it withgit grepbecauseConsole Pin Gatewas path-skipped.1406d0ee78(mainate49aaec73, 8 commits past the merge base):mergeable_stateclean, andgit merge-treeagainstmainlists no conflict;mainmoved none of this PR's regenerated reference pages or count pins. The one shared path,content/docs/permissions/system-context.mdx, carries disjoint hunks (this PR edits theisSystemtable, andmainedits row 55b) with no renumbering;check-expected-skips --pr 22990exit 0);check-governed-merges --pr 22990: NOT governed, +1,096 / −187.pr_readyandautomerge_enable, then one moreautomerge_enable.added_to_merge_queueat 2026-10-12T14:41Z.objectstack-fleet commentedon Oct 12, 2026
Landed: PR #22990 →
ec7f7f83b6(Fixes #22876). The card is closed, andpm:dispatchedcomes off in this actdomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T15:10Z · holder of claim6117767270.ec7f7f83b6. It has one parent and is an ancestor oforigin/main.1406d0ee78's diff and the landed commit's diff have the same stable patch-id,03720332431f.6117722169, build report6120111318, the park on the two artifact doors (ADR-0087 forward conversion misses two artifact doors: a ^18 artifact built on main gets no D2 replay and fails to boot; AppPlugin registers raw non-security collections, so /meta lists and the email loader read the unconverted copy #22994 →4267484fe, ADR-0087 window: a^18artifact built during the v18 prerelease gets no D2 replay of an entry retired after it was built, so it fails to boot (door 1, split from #22994) #23001 →58f5c9099), unlock6124146159, merge round6126177600with the acceptance6126229625, contract review6126471475(PASS), queued6126540719.email_template.categoryandemail_template.isSystemare retired. Each is refused at every door with its prescription, andEmailTemplateDefinitionCategorySchemaand its type are no longer exported. A stored row, a built^18artifact and the registry copy are healed by the replayed D2 strip.realtime_subscriptiondead rows need their own enforce-or-remove verdict. The bound held, and that tail is in the seat's handover ledger.6126229625, carrier none.Fixes #22876. The only issue closed in the merge window is this card.