Follow-up filed by the domain:spec seat on landing #14369 (PR #14638, squash a3d5724c8, 2026-09-02T18:37Z). Filed unassigned and unlabelled for triage; the seat leaves domain:* and the pm-state to the triage seat (single-producer rule).
What #14369 settled, and what it deliberately did not
#14369 was ruled a LEDGER-only card: it enrolled CrudEndpointsConfigSchema, MetadataEndpointsConfigSchema, BatchEndpointsConfigSchema and RouteGenerationConfigSchema in packages/spec/liveness/{crud_endpoints,metadata_endpoints,batch_endpoints,route_generation}.json (32 rows: 17 live with path#symbol evidence and a producer pointer, 15 dead with evidenceScope: in-repo, verifiedAt = merge base 2514d49f3). It removed, enforced, deprecated or re-described no key — that was the ruling, and the PR kept to it.
The 15 dead rows are the input to the ADR-0049 enforce-or-remove route (the spec-property-retirement playbook), which nobody has opened yet. This card is that door. Per key, measured at 2514d49f3 (all 0 read sites in packages/rest/src non-test sources outside normalizeConfig and the NormalizedRestServerConfig type; repo-wide grep outside packages/spec returns only changesets, the generated reference page and #11984's own refusal tests; objectui d4c6a86 clean):
| ledger |
key (rows) |
note carried in the ledger |
route_generation |
routes.includeObjects · routes.excludeObjects · routes.nameTransform · routes.overrides (3 rows: enabled / basePath / operations) |
nameTransform is validated against its enum and still mounts exactly what 'none' mounts |
crud_endpoints |
crud.patterns (4 rows: method / path / summary / description) · crud.objectParamStyle |
— |
metadata_endpoints |
metadata.cacheTtl · metadata.endpoints.schema |
cacheTtl accepts a negative bound (recorded in the row, not filed separately); endpoints.schema gates a route that does not exist (packages/rest/src mounts no path ending in /schema) |
batch_endpoints |
batch.defaultAtomic · batch.operations.upsertMany |
upsertMany is the twin of a protocol member that does not exist (createManyData / updateManyData / deleteManyData only) |
RestApiConfigSchema (the api sub-object) is NOT enrolled and not in this card: its seam is #14366's, and its own dead keys (api.documentation, api.responseFormat) are #14640.
What this card asks
An enforce-or-remove verdict per key (or per family), following the retirement playbook's route choice and ledger discipline — the closed-set cloud sweep that the in-repo evidence could not reach is part of the measurement. #11984's distinction holds throughout: normalizeConfig now parses and consumes these sub-objects, which settles accept/reject; executing a declared contract does not give a key a consumer.
Coupling
Blocked-by: (resolved 2026-09-03T09:52Z — PR #14871 quarantined the #14832 hang on main; PR #14868 rebuilt on the new base)
Unlock-action: (fired 2026-09-03T09:52Z — re-checked, landing)
(Added by the domain:spec seat 2026-09-03T02:16Z: the closed-set cloud sweep triage made a stop-the-line precondition is a reading only the repo:cloud seat can take — objectstack-ai/cloud is unreachable from this seat; the unlock scan returns this card when #14796 closes.)
(Added by the domain:spec seat 2026-09-03T07:30Z: work complete — PR #14868 contract review PASS 5522043706, gate cleared on both carriers, carriers check exit 0, trial merge clean; PR-side CI red twice on the same line — the packages/cli run-dev-unbuilt-workspace.e2e.test.ts hang anchored on #14832 — so the single permitted re-run is spent; re-check the PR when #14832 closes, do not re-dispatch.)
Generated by Claude Code
Follow-up filed by the
domain:specseat on landing #14369 (PR #14638, squasha3d5724c8, 2026-09-02T18:37Z). Filed unassigned and unlabelled for triage; the seat leavesdomain:*and the pm-state to the triage seat (single-producer rule).What #14369 settled, and what it deliberately did not
#14369 was ruled a LEDGER-only card: it enrolled
CrudEndpointsConfigSchema,MetadataEndpointsConfigSchema,BatchEndpointsConfigSchemaandRouteGenerationConfigSchemainpackages/spec/liveness/{crud_endpoints,metadata_endpoints,batch_endpoints,route_generation}.json(32 rows: 17livewithpath#symbolevidence and aproducerpointer, 15deadwithevidenceScope: in-repo,verifiedAt= merge base2514d49f3). It removed, enforced, deprecated or re-described no key — that was the ruling, and the PR kept to it.The 15
deadrows are the input to the ADR-0049 enforce-or-remove route (thespec-property-retirementplaybook), which nobody has opened yet. This card is that door. Per key, measured at2514d49f3(all 0 read sites inpackages/rest/srcnon-test sources outsidenormalizeConfigand theNormalizedRestServerConfigtype; repo-wide grep outsidepackages/specreturns only changesets, the generated reference page and #11984's own refusal tests; objectuid4c6a86clean):route_generationroutes.includeObjects·routes.excludeObjects·routes.nameTransform·routes.overrides(3 rows:enabled/basePath/operations)nameTransformis validated against its enum and still mounts exactly what'none'mountscrud_endpointscrud.patterns(4 rows:method/path/summary/description) ·crud.objectParamStylemetadata_endpointsmetadata.cacheTtl·metadata.endpoints.schemacacheTtlaccepts a negative bound (recorded in the row, not filed separately);endpoints.schemagates a route that does not exist (packages/rest/srcmounts no path ending in/schema)batch_endpointsbatch.defaultAtomic·batch.operations.upsertManyupsertManyis the twin of a protocol member that does not exist (createManyData/updateManyData/deleteManyDataonly)RestApiConfigSchema(theapisub-object) is NOT enrolled and not in this card: its seam is #14366's, and its own dead keys (api.documentation,api.responseFormat) are #14640.What this card asks
An enforce-or-remove verdict per key (or per family), following the retirement playbook's route choice and ledger discipline — the closed-set
cloudsweep that the in-repo evidence could not reach is part of the measurement. #11984's distinction holds throughout:normalizeConfignow parses and consumes these sub-objects, which settles accept/reject; executing a declared contract does not give a key a consumer.Coupling
CrudEndpointsConfigSchema.patternsis an exhaustive enum-keyedz.record— the input type demands all five operations and a partial parses to explicitundefinedentries (wantsz.partialRecord) #14365 (CrudEndpointsConfigSchema.patternswantsz.partialRecord;RouteGenerationConfigSchema.overrides.*.operationslikewise) isBlocked-by:this card: reshaping a record that this verdict may remove is the wasted work the ledger step exists to prevent (triage's own note onCrudEndpointsConfigSchema.patternsis an exhaustive enum-keyedz.record— the input type demands all five operations and a partial parses to explicitundefinedentries (wantsz.partialRecord) #14365). If the verdict forcrud.patterns/routes.overridesis enforce,CrudEndpointsConfigSchema.patternsis an exhaustive enum-keyedz.record— the input type demands all five operations and a partial parses to explicitundefinedentries (wantsz.partialRecord) #14365 is the first move; if remove,CrudEndpointsConfigSchema.patternsis an exhaustive enum-keyedz.record— the input type demands all five operations and a partial parses to explicitundefinedentries (wantsz.partialRecord) #14365 closes with the removal and its measurement is the evidence.RestApiConfig.documentationandresponseFormatare normalized byRestServerand read by nothing — two more ADR-0049 candidates on the oneRestServerConfigsub-object the liveness ledger does not cover #14640 (api.documentation/api.responseFormat) is the same class on the unenrolledapisub-object — sibling, not a child.packages/rest/src/rest-sub-config-parse-not-cast.test.ts(RestServer.normalizeConfigstill castscrud/metadata/batch/routesinstead of parsing them —batch.maxBatchSizebounds and three declared enums never execute #11984) pins today's declared behaviour of these keys; a removal reverses those pins by design (director-seat note onCrudEndpointsConfigSchema.patternsis an exhaustive enum-keyedz.record— the input type demands all five operations and a partial parses to explicitundefinedentries (wantsz.partialRecord) #14365, 5507239846).Blocked-by: (resolved 2026-09-03T09:52Z — PR #14871 quarantined the #14832 hang on main; PR #14868 rebuilt on the new base)
Unlock-action: (fired 2026-09-03T09:52Z — re-checked, landing)
(Added by the
domain:specseat 2026-09-03T02:16Z: the closed-setcloudsweep triage made a stop-the-line precondition is a reading only therepo:cloudseat can take —objectstack-ai/cloudis unreachable from this seat; the unlock scan returns this card when #14796 closes.)(Added by the
domain:specseat 2026-09-03T07:30Z: work complete — PR #14868 contract review PASS 5522043706, gate cleared on both carriers, carriers check exit 0, trial merge clean; PR-side CI red twice on the same line — thepackages/clirun-dev-unbuilt-workspace.e2e.test.tshang anchored on #14832 — so the single permitted re-run is spent; re-check the PR when #14832 closes, do not re-dispatch.)Generated by Claude Code