Uh oh!
There was an error while loading. Please reload this page.
feat(spec): retire the ten inert RestServerConfig keys the liveness ledger recorded as dead (ADR-0049 enforce-or-remove) - #14868
Conversation
Tombstone crud.patterns/objectParamStyle, metadata.cacheTtl/endpoints.schema, batch.operations.upsertMany/defaultAtomic and all of routes.* under ADR-0049 enforce-or-remove (#14369 census); drop the orphaned CrudEndpointPatternSchema; keep the ledger rows dead with REMOVED notes; reverse the #11984 pins. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
…e artifacts Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
…or census docs; re-record test-typecheck debt Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
…st-server-config-dead-keys
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
📓 Docs Drift CheckThis PR changes 2 package(s): 32 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cbf712266dff7477403bf68c67762acf6b68e97c && git checkout cbf712266dff7477403bf68c67762acf6b68e97c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a6d278ba263fbf12dd859dc54820d335c62f52f9 ba875f3fbd4dd25c22de9bbc89af44e4a54c16c9 && git checkout -B drift-repro a6d278ba263fbf12dd859dc54820d335c62f52f9 && git merge --no-ff ba875f3fbd4dd25c22de9bbc89af44e4a54c16c9
node scripts/docs-audit/affected-docs.mjs --json a6d278ba263fbf12dd859dc54820d335c62f52f9
|
os-sam
commented
Sep 3, 2026
CI:
Generated by Claude Code |
CI on That is #14832 — the Dev session: no action needed on this branch for the CI red; the review verdict lands on the card. Generated by Claude Code |
os-sam
commented
Sep 3, 2026
Standing down until #14832 closes — the single permitted re-run is spent; nothing on this branch needs to change. Attempt 2 of run 33723979376 ( State: contract review PASS (5522043706 on #14691), Generated by Claude Code |
…st-server-config-dead-keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
…ps://github.com/objectstack-ai/objectstack into claude/issue-14691-rest-server-config-dead-keys
Fixes#14691
Retires, under ADR-0049 enforce-or-remove, the 15
RestServerConfigrows the liveness ledger recorded asdeadon #14369's landing (PR #14638). Every family resolved to REMOVE; the PM's two ENFORCE candidates (routes.overrides,crud.patterns) were measured and falsified, so no split card was filed. Clause ②: this narrows the accept set of published authorable keys, so the PR opens as draft withneeds:contract-reviewfor the seat review.Route (H1 — measured)
All four sub-schemas are non-strict
z.object()(source; and #11984's own §C pin "STRIPS an unknown key … the schemas are non-strict" was green on the base). Route:retiredKey()tombstones, ledger rows keptdeadwith the REMOVED note template, two refusal channels (tscnever+ parse prescription). One value def became an orphan (CrudEndpointPatternSchema, sole consumercrud.patterns) and left with its key — the only whole-def removal, declared inRETIRED_DEFS_BY_MAJOR[18].CrudOperationstays:GeneratedEndpointSchema.operationstill reads it (the first attempt to remove it was caught by that read, so it was restored).Per-family verdicts (H2 — measured, all REMOVE)
crud.patterns(4:method/path/summary/description)registerCrudEndpoints. The mounted paths ARE the contract the client SDK (packages/client/src/index.tshard-codes/data/${object}…), the discovery document (routes.data) and the served/openapi.json(pre-generated contract, enriched) all describe — a per-operation method/path knob could only make them lie (Route & surface ownership §4). The live door for a custom path/method/summary is a declarativeapiendpoint (type: 'object_operation', matched/executed/documented — theapiledger).patterns; the 4 child rows collapse into one leaf row (children left the walked shape → would report ORPHAN);CrudEndpointPatterndef removedcrud.objectParamStyle'query'mounted what'path'mountsmetadata.cacheTtlenableCacheselects the protocol'sgetMetaItemCachedpath, which takes no TTL; no cache header built from it. Negative-bound defect dies with itmetadata.endpoints.schemaGET /meta/:type/:name/schema, which does not exist (no/schemamount inpackages/rest/src)endpointsblock, row keptbatch.defaultAtomicoptions.atomic(BatchOptionsSchema, ADR-0119 D4: opt-in, defaultfalse, deliberately aligned to what callers get). A server default that flips it silently changes failure semantics for callers who send nothing — the move ADR-0119 D4 refused. Enforce would re-open a settled decisionbatch.operations.upsertManyupsertManyroute, no protocol member; upsert exists as an operation TYPE of the genericPOST /data/:object/batch(BatchOperationType 'upsert'), gated byenableBatchEndpointroutes.includeObjects+excludeObjectsrest-server.ts#enforceApiAccess(enable.apiEnabled: false→ 404,enable.apiMethodswhitelist → 405). A server-config selector is a second, unread dialect of an enforced contract (PD #12)@exampleadvertisingroutes: { excludeObjects: [...] }correctedroutes.nameTransformnameis the canonical id on every surface incl. the REST path segment (PD #6) — a URL transform contradicts the one-name ruleroutes.overrides(3:enabled/basePath/operations)enabled/operationsduplicateenable.apiEnabled/enable.apiMethods(enforced, per object, on the object);basePathper object contradicts the one deployment-wide data basecrud.dataPrefixthe discovery document advertises. Nothing to build — the feature exists where the contract belongsoverrides; 3 child rows collapse into one leaf rowFour-axis reading for the two falsified candidates: real business need — zero authors in-repo, objectui @d4c6a86, cloud @9b6abe0f2fd5 (structural: cloud never authors a
RestServerConfig); long-term soundness — both would fork a contract that already has one enforced home (object exposure keys; declarative endpoints), and per-deployment CRUD path shapes break every SDK/discovery/OpenAPI consumer; AI-error-proofing — one declared seat per capability beats a server-side dialect an author can pick wrongly; startup scope — zero pull, no expansion.Coupling:#14365 (
z.partialRecordforcrud.patterns/overrides.*.operations) closes with the removal — there is no record left to reshape; its measurement is cited on the retired-key entries. Not closed here (the PM does). #14640 (api.*) untouched. The #11984 pins are REVERSED into refusal pins (§E ofrest-sub-config-parse-not-cast.test.ts), never deleted; the live-key regression guards are unchanged.Ledger discipline
status: deadkept,verifiedAt: 2026-09-03,evidenceScope: cross-repo(the vocabulary isin-repo | cross-repo; the dispatch's literalin-repo + cloud@…would fail the gate, so the cloud/objectui pins are in thenote), house REMOVED note template + what to do instead + the pre-retirement census note preserved.patterns,overrides) collapse to leaf rows: aretiredKey()is a leaf, so child rows would report ORPHAN / "children on a non-container".state-counts.mdregenerated (crud_endpointsdead 5→2,route_generation6→4, total dead 89→84).check:liveness✓ (all 35 types),check:empty-state✓ after removing therest-server.zod.ts includeObjectsscopeentry (its "(empty = all)" statement left with the key — the gate would otherwise report STALE).ADR-0087 (H6)
No D2 conversion: a
RestServerConfigis plugin TS configuration (REST plugin constructor /plugin-hono-serverrestConfig), never a stack collection member or asys_metadatarow — theopenApi31(#4579) andMetadataPluginConfig.additionalTypesprecedents — so there is no source for the chain to rewrite and the prescriptions carry noos migrate metasentence (the sentence pin only judges sentences that are present). Registered under major 18 (launch-window convention): 10entries/retired-keys/18.api__*.ts, 1entries/retired-defs/18.api__CrudEndpointPattern.ts, 1entries/semantic/18.rest-server-config-dead-keys-retired.ts;gen:migration-registryregeneratedregistry.ts. Gate (b) accepted the 8 surface-level keys as[RETIRED]lines; the two nested keys (endpoints.schema,operations.upsertMany) have no surface line of their own (thekernel/Manifest:contributes.routesshape) and are registered the same way.Ratchets (H4 — expectation decided from the route BEFORE reading)
authorable-surface/api.json[RETIRED]; whole-def removal → 4CrudEndpointPattern:*lines disappear (gate (a) tripwire, deleted deliberately)json-schema.manifest/api.jsonRETIRED_DEFS_BY_MAJORapi/CrudEndpointPattern.json; line deleted deliberately; accepted "RETIRED_DEFS_BY_MAJOR, major 18"api-surface/api.jsonCrudEndpointPattern (type),CrudEndpointPatternSchema (const)); tombstones invisibleexport-origins/,declaration-map/check:generated --fix)authorable-defaults/api.jsoncontent/docs/references/api/rest-server.mdx,references/index.mdxCrudEndpointPatternsection gone, tombstone[REMOVED]descriptionsdocs/audits/…strictness-ledger.counts.md,liveness/state-counts.mdspec-changes.json,docs/protocol-upgrade-guide.mdcheck:spec-changes/check:upgrade-guide✓ up to date (the D3 semantic entry does not feed either)test-typecheck-debt.jsongen:test-typecheck-debt(SHRANK, not maintainer-only)type-alias-convention.pin.test.tsIso188), count 832→831 with history notecontent/docs/permissions/system-context.mdxrest-server.tsrot by −2 (type block shrank)check-system-context-census --fixre-anchored 5 anchors; recheck ✓normalizeConfig(H3)NormalizedRestServerConfiglosescrud.patterns/objectParamStyle,metadata.cacheTtl/endpoints.schema,batch.defaultAtomic/operations.upsertMany;routesbecomes an empty record type (aRecordof string to never) (still parsed, so an authored key is refused with its prescription; nothing threaded). No live key's threading changed — the 17 live rows are the regression set and their pins (§C/§D) are unchanged and green.mainwas the base atf3ae441fa; #14801/#14677 had not landed, so no merge was needed — hunks are confined to the type block, the threading, one docblock line and one import.Changeset
.changeset/rest-server-config-dead-keys-retired.md—@objectstack/spec: minor,@objectstack/rest: minor(the launch-window conventioncheck-changeset-no-majorenforces: breaking ships asminor; the BREAKING banner + the ADR-0087 marker are the carriers), the ADR-0087 disposition markeradr-0087: registered rest-server-config-dead-keys-retired(as an HTML comment in the changeset body), FROM → TO per key with the one-line fix, the retirement kit paragraph.Verification record
CI is GREEN on the current head
ba875f3— all six required contexts pass (Lint & Repo Gates,TypeScript Type Check,Test Core,Dogfood Regression Gate,Build Core,Temporal Conformance (live PG + MySQL)); 33 success / 5 skipped / 0 failure.mainhas been merged three times since the first push. The one red this PR ever carried wasTest Core (1/6)›packages/cli›run-dev-unbuilt-workspace.e2e.test.ts, the never-read-reader hang — not this PR's: it was red onmainat2263ca4, the base this branch merged, and its cause is #14832 (os devparks in a blockingwrite(2)on fd 2; ~27/30 on a cold-tsx-cache runner, which is every CI runner). It cleared here by mergingmainonce392f410(#14871, the maintainer's ruling-A quarantine of that single case) landed; the product fix is #14875, still in flight in its own lane. Nothing was skipped, quarantined or re-run to get this PR green — the standing-down comment on the failure is 5521890188.The per-check readings below are from the tree at head
3c53eda(the last commit changes only the ten prescription strings, the regeneratedrest-server.mdx, the census anchors and the test-typecheck ledger; every gate and suite named was re-run after it, on that tree). Every command went throughscripts/pm/os-verify-lock.sh; verdict lines quoted from the gates' own output.pnpm --filter @objectstack/spec buildVERDICT command-exit 0— gen:schema: "authorable-surface/ updated (8055 keys)", manifest deletion gate: "json-schema/api/CrudEndpointPattern.json — RETIRED_DEFS_BY_MAJOR, major 18"pnpm --filter @objectstack/spec check:generated(after--fix)check:spec-changes/check:upgrade-guide/check:authorable-surface/check:api-surface/check:docs/check:liveness/check:strictness-ledger/check:test-typecheck… all ✓)pnpm --filter @objectstack/spec check:livenesscrud_endpoints 8 (live 6, dead 2),metadata_endpoints 8 (6/2),batch_endpoints 7 (5/2),route_generation 4 (dead 4)pnpm --filter @objectstack/spec check:empty-statepnpm --filter @objectstack/spec typechecktsc --noEmit✓,check:scripts-typecheck✓,check:test-typecheck"OK — 54 file(s) / 261 error(s) / 145 pinned signature(s)" after the SHRANK re-record (one TS18048 on the deleted "custom patterns" test VANISHED)pnpm --filter @objectstack/spec test(full)VERDICT command-exit 0pnpm --filter @objectstack/rest typecheckVERDICT command-exit 0(tsc --noEmit && check:test-typecheck)pnpm --filter @objectstack/rest test(full)VERDICT command-exit 0rest-server.test.ts+retired-key-migrate-sentence.test.ts+migrations.test.ts: 178 passed; restrest-sub-config-parse-not-cast.test.ts: 25 passed (§E refusal pins green; the reversed §A/§C/§D cases removed by design)node scripts/pm/dispatch-gates.mjs --commands(no paths; derived from the merge-base at04f4c1f)check-system-context-census(REAL — line rot incontent/docs/permissions/system-context.mdxfrom the shrunk type block;--fixre-anchored 5 anchors, recheck ✓);check:doc-authoring(REAL —#14691inside the ten prescriptions; removed, recheck ✓);check:api-surface/check:dual-source-exports/check:entry-nameability/check:exported-any/check:generated(dist-freshness prerequisite after test-file edits; rebuilt and re-run, all exit 0);check:skill-examples(neededclient-reactdist; built, re-run, exit 0);check-dev-prereqs,check:dual-build-cjs-loads,check:type-check-debt(PREREQUISITE NOT MET — 41 unbuilt package dists in this worktree, no fullpnpm build; NOT MEASURED locally, CI reads them);check-test-completeness(exit 3, needs aturbo run testlog — NOT MEASURED locally by design)api-surface/api.json−2,authorable-surface/api.json8 flips to[RETIRED]+ 4 lines gone,json-schema.manifest/api.json−1,state-counts.mddead 89→84 — all as expected from the route (table above)Not run:
pnpm lintrepo-wide (CI-owned run). Not applicable: forms / i18n (no*.form.tsauthors aRestServerConfig),check:variant-docs(no union removed; it ran green in the gate family anyway).Out of scope / not touched
RestApiConfigSchema(api.documentation/api.responseFormat, [finding]RestApiConfig.documentationandresponseFormatare normalized byRestServerand read by nothing — two more ADR-0049 candidates on the oneRestServerConfigsub-object the liveness ledger does not cover #14640) — sibling, untouched.content/docs/releases/— untouched.CrudEndpointPattern(repo scope is objectstack only); the [finding] Ten declaredRestServerConfigkeys are normalized byRestServerand read by nothing —routes.*entirely,crud.patterns/objectParamStyle,metadata.cacheTtl/endpoints.schema,batch.defaultAtomic/operations.upsertMany(ADR-0049 enforce-or-remove candidates) #14369 census recorded objectui @d4c6a86 clean for every key, and theConsole Pin Gateon this PR is the mechanical reading for the def.🤖 Generated with Claude Code
https://claude.ai/code/session_01J5bnauQVfR8NbAm4QagXzj
Generated by Claude Code
Generated by Claude Code
Generated by Claude Code