Skip to content

feat(spec)!: retire the docs-viewer keys — ApiDocumentationConfig.ui and the REST API plugin's openApi.uiPath / uiFramework (ADR-0049) - #25180

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-25159-api-docs-viewer-keys-retired
Oct 22, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-25159-api-docs-viewer-keys-retired

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #25159

Clause-②: no (narrowing)
Metadata-protocol: narrowed

Dispatched by the domain:spec seat 1 PM (claim 6333051310), executed through the spec-property-retirement kit. Ruling executed: triage route A, record 6332269878 (retire with tombstones and an ADR-0087 entry, no staged window), following the maintainer's 「C 下线页面」 on #25104 (6317642533).

What changed

Three keys declared an HTML API documentation viewer that nothing ever served. Each is now a retiredKey() tombstone: a tsc error at the authoring site and a parse error at the key's path whose message is the prescription.

Key (authored as) Def key Was
ApiDocumentationConfig.ui api/ApiDocumentationConfig:ui ApiTestingUiConfigSchema.optional(), "Testing UI configuration"
RestApiPluginConfig.openApi.uiPath api/OpenApiGenerationConfig:uiPath z.string().default('/api/docs'), "URL path to serve documentation UI"
RestApiPluginConfig.openApi.uiFramework api/OpenApiGenerationConfig:uiFramework z.enum(['swagger-ui','redoc','rapidoc','elements']).default('swagger-ui')

The orphan value defs api/ApiTestingUiConfig and api/ApiTestingUiType leave whole, with their exports ApiTestingUiConfigSchema, ApiTestingUiConfig, ApiTestingUiConfigParsed and ApiTestingUiType (kit §4, "orphan value schema").

Each prescription names the mechanism that is live: the OpenAPI document is served at GET /api/v1/openapi.json under the REST server's default api.basePath and api.version (the path follows both) while api.enableOpenApi is on. Example, uiPath: "RestApiPluginConfig.openApi.uiPath was removed in @objectstack/spec 18.1.0 (ADR-0049 enforce-or-remove) — nothing ever read it: no documentation viewer was ever served at its path, the /api/docs default included, and the platform serves no HTML API documentation viewer at all, only the machine-readable OpenAPI document. Delete the key. The OpenAPI document is served at …"

uiFramework: an in-place addition, not named on the card

uiFramework sits one line below uiPath on the same schema and selects the viewer uiPath would have served. All four in-place conditions hold: ① same defect class (a viewer declaration with no viewer); ② mechanical, with the shape fixed by uiPath beside it; ③ the file is inside this claim's surface; ④ same gates, no new verification surface. Evidence: zero identifier hits outside packages/spec here and in objectui at its pin (readings below). Leaving it would keep the declaration the ruling removed: a framework choice for a viewer the platform does not serve, with a swagger-ui default materialised on every parse. It is a third narrowed key, so the maintainer report for this round should list it.

Hypotheses, as measured

  • H1 (no reader): holds. At origin/main b93c750c7, git grep -nE "ApiTestingUi|uiPath|documentation\.ui" outside packages/spec, content/docs/references and CHANGELOG.md finds 2 hits, both packages/rest/src/ui-routes.ts:79,85: a local uiPath variable for the unrelated /ui/view route. uiFramework, enableTryItOut and OpenApiGenerationConfig find 0 each. Controls on the same instrument: enableOpenApi 16 and generateOpenApi 8. objectui at the pinned .objectui-sha 56cf4d8e89: 0 for ApiTestingUi|uiPath|uiFramework|documentation\.ui|ApiDocumentationConfig|OpenApiGenerationConfig|enableTryItOut, against controls @objectstack/spec/api 103 and openapi\.json 12. So no objectui follow-up is owed. Inside packages/spec, nothing parses ApiDocumentationConfigSchema, OpenApiGenerationConfigSchema or RestApiPluginConfigSchema outside their own unit tests. The only importer is the api barrel.
  • H2 (which def): holds. uiPath is on OpenApiGenerationConfigSchema (api/OpenApiGenerationConfig:uiPath), which RestApiPluginConfigSchema.openApi embeds, so an author writes RestApiPluginConfig.openApi.uiPath. The pin asserts the refusal at both doors, the second located at openApi.uiPath.
  • H3 (orphans): holds. ApiTestingUiConfigSchema / ApiTestingUiType have no consumer besides ui. In this tree the only other references were their own unit tests and the ADR-0122 isomorphic pin line. They leave whole, so api-surface/ moves.
  • H4 (served path): refined. The card's "/openapi.json" is incomplete. registerRoutes calls registerOpenApiEndpoints(getApiBasePath()) while api.enableOpenApi is on, and getApiBasePath() = apiPath ?? basePath/version, so the route is GET /api/v1/openapi.json by default (packages/rest/src/openapi-endpoints-read-decorations.test.ts:221 reads that route). The prescription spells the default path and says it follows api.basePath / api.version.
  • H5 (family entry): own family. New D3 entry api-docs-viewer-keys-retired. It is not folded into rest-api-config-dead-keys-retired (PR docs(spec): name only the OpenAPI document where the retired /docs viewer page was named #25162), for three reasons. Those keys sit on RestApiConfigSchema, the REST server's construction argument, and were dead in the rest_api liveness census. These sit on two other schemas and retire because the viewer was taken offline. Different surface, different ruling. That landed entry is not edited.
  • H6 (sibling estimate): re-measured, holds. The H1 readings above were re-taken on this branch's base, not inherited.

Kit surfaces

  • Schema: three tombstones with retirement records in documentation.zod.ts / plugin-rest-api.zod.ts, and the module and @example docblocks no longer show a viewer.
  • Orphan defs: both deleted. The json-schema.manifest/ ratchet fired first, as the kit predicts for a whole-def removal, and the two manifest keys are removed deliberately.
  • ADR-0087: RETIRED_KEYS_BY_MAJOR[18] gets 3 entry files and RETIRED_DEFS_BY_MAJOR[18] gets 2. There is one D3 semantic entry, plus its STEP18_RATIONALE fragment (order: 108, inserted at its id's sort position). No D2 conversion: neither config is a stack collection member or a sys_metadata row, so no conversion seam would ever see one. This is the kernel/Manifest:loading disposition, the same one the handlerStatus and rest-api-config retirements on these files took. The defaults on uiPath / uiFramework leave no residue window, because nothing parses this config into a stored or built artifact. The old defaults are refused like any other value.
  • Liveness ledger: n/a. No ledger walks ApiDocumentationConfigSchema or OpenApiGenerationConfigSchema: neither is a registry type nor in SPEC_ONLY_SCHEMAS. So there is no row to keep dead.
  • Generated: authorable-surface/api.json has 3 [RETIRED] rows and the 12 api/ApiTestingUiConfig:* rows deleted. Gate (a) printed them, and check (c) accepted the deletion because the whole def leaves. Also regenerated: authorable-defaults/api.json (10 default rows), json-schema.manifest/api.json (2 defs), api-surface/api.json (5 exports), export-origins/ and declaration-map/, the strictness-ledger counts, and the reference pages api/documentation, api/plugin-rest-api and references/index (1544 to 1542 schemas). All of these came from check:generated --fix.
  • Forms / i18n / CLI advisory lint / examples / published skills / hand-written docs: none teach either key (grep, 0 hits).
  • REST_API_PLUGIN.md: the example drops uiPath / uiFramework, the "Multiple UI frameworks" feature bullet is gone, and a paragraph names the document path.
  • Spec Shape Gate registry: not owed. Its $comment ties an entry to a downstream objectui card, and objectui has zero readers.
  • Changeset: .changeset/25159-api-docs-viewer-keys-retired.md, @objectstack/spec minor, with both declaration lines, the ADR-0087 marker (registered api-docs-viewer-keys-retired), the BREAKING banner, FROM → TO and the one-line fix.

Tests

  • New pin packages/spec/src/api/docs-viewer-keys-retirement.test.ts (22 cases, in the repo vitest project because it reads outside the package):
    • code invalid_type, path and the prescription at each door, for every former value including the old defaults;
    • the tsc channel, with three @ts-expect-error checks (the file is in tsconfig.test.json's program; --listFilesOnly finds it);
    • live controls;
    • zero holders for the 4 retired names, plus survivors;
    • the ADR-0087 registration;
    • a tree-scoped absence walk with two legs: references to retired names, and structural authorings of uiPath / uiFramework / a viewer ui block.
    • Radius: the five roots already declared for @objectstack/spec in scripts/cross-package-test-inputs.mjs and turbo.json. One structural exclusion, authorable-surface.base.json: the published-surface anchor, which only gen:authorable-surface-base writes. check:cross-package-test-inputs is green with the pin added to vitest.repo-tests.json.
  • Edited: documentation.test.ts and plugin-rest-api.test.ts no longer author the retired keys. type-alias-convention.pin.test.ts drops Iso_api_documentation__ApiTestingUiType, and its count goes from 789 to 788, with the history line added.
  • Reverse verification (committed first, run under scripts/ablation-replace.mjs): uiPath: retiredKey(...) was put back to z.string().default('/api/docs'). Predicted direction red; observed red, 6 failed / 16 passed of 22. Restored blob f365b815fca0 equals HEAD and git diff HEAD is empty.
  • Walk probe: an untracked examples/app-crm/zz-probe-25159.yaml authoring uiPath, plus a content/docs/zz-probe-25159.md fence importing ApiTestingUiConfigSchema. Both legs fired (...yaml:3 authors a retired docs-viewer key, ... references { ApiTestingUiConfigSchema }). The probes were deleted afterwards and the tree was clean.

Readings at head 21ed122c3

The head is this branch merged with origin/main ab488d9b7, which moved no packages/spec path.

  • pnpm --filter @objectstack/spec test (vitest local project): Test Files 706 passed (706), Tests 21456 passed and 1 todo (21457).
  • The repo-project files this diff touches or reads (docs-viewer-keys-retirement, scripts/step18-rationale-merge, shared/retired-key-migrate-sentence, api/rest-api-config-dead-keys-retirement): Test Files 4 passed (4), Tests 69 passed (69). The rest of test:repo is CI's.
  • pnpm --filter @objectstack/spec typecheck: exit 0, test layer included (check:test-typecheck OK, no new debt).
  • pnpm --filter @objectstack/spec check:generated: exit 0.
  • Gates from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths: 113 derived, 113 run, all exit 0. --ran reports "113 derived famil(ies) accounted for, 113 run, 0 NOT-MEASURED".
    • Eleven first refused with exit 3. Six were spec gates over a dist older than my last test-file edits; four needed @objectstack/lint / @objectstack/objectql / every package built (check:dual-build-cjs-loads); one, the teardown self-test, needed a pinned commit the shallow clone lacked. A fresh check:generated also reported the same stale dist (exit 1).
    • All twelve were re-run green after the spec build, the lint and objectql closure builds, a turbo build (77/77, 75 cached), and a depth-1 fetch of 621a48760.
  • Outside the derivation: pnpm check:dev-prereqs exit 0; node scripts/check-platform-checklist.mjs --changed-from origin/main exit 0 (0 anchored files in the change set).
  • Dogfood battery, once on this head, pnpm --filter @objectstack/dogfood test under the verify lock, no shard: VERDICT command-exit 0, Test Files 287 passed and 3 skipped (290), Tests 2395 passed and 18 skipped (2413).
  • Consumer reading, direction downstream (...@objectstack/spec): measured by the tree-scoped pin and the grep with controls above, not by rebuilding the consumer closure (kit §4). Zero readers outside packages/spec.

Acceptance notes

  • OpenApiGenerationConfigSchema and RestApiPluginConfigSchema as a whole, and every other key of ApiDocumentationConfigSchema, are parsed by nothing outside their own unit tests in this tree. The REST plugin's real config is the separate RestApiPluginConfig interface in packages/rest, which carries api?: RestServerConfig. Example: openApi.outputPath defaults to /api/docs/openapi.json, while the document is served at GET {basePath}/openapi.json. This is a dormant, zero-pull declaration, out of this card's scope. It is noted here and not filed: no named producer and no measured public-door reach.

Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 22 documentable anchor(s). ⚠️ 13 changed file(s) yielded no anchor (packages/spec/REST_API_PLUGIN.md, packages/spec/api-surface/api.json, packages/spec/authorable-defaults/api.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/api/client-sdk.mdx (via getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), meta.getView (sdk, the route ledger binds it to GET /api/v1/ui/view/:object/:type, selected by route anchor /view/:object/:type))
  • content/docs/api/metadata-api.mdx (via /view/:object/:type (route, bridged from symbol uiPath — its route source's handler names it))
  • content/docs/api/plugin-endpoints.mdx (via /view/:object/:type (route, bridged from symbol uiPath — its route source's handler names it))
  • content/docs/kernel/contracts/metadata-service.mdx (via getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type))
  • content/docs/kernel/services-checklist.mdx (via getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), /view/:object/:type (route, bridged from symbol uiPath — its route source's handler names it))
  • content/docs/protocol/objectui/concept.mdx (via /view/:object/:type (route, bridged from symbol uiPath — its route source's handler names it))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), meta.getView (sdk, the route ledger binds it to GET /api/v1/ui/view/:object/:type, selected by route anchor /view/:object/:type))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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

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

Which tree this was computed on

This run read content/docs from 85ad6a36605d770dec7ce9d6a8607413d4247bf5 — the merge of head 21ed122c3a750ba711a08c78c8f2d54c3ef39d4c into base 72a302a9dde3347e15a971c5da28df205496a217, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 85ad6a36605d770dec7ce9d6a8607413d4247bf5 && git checkout 85ad6a36605d770dec7ce9d6a8607413d4247bf5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 72a302a9dde3347e15a971c5da28df205496a217 21ed122c3a750ba711a08c78c8f2d54c3ef39d4c && git checkout -B drift-repro 72a302a9dde3347e15a971c5da28df205496a217 && git merge --no-ff 21ed122c3a750ba711a08c78c8f2d54c3ef39d4c

node scripts/docs-audit/affected-docs.mjs --json 72a302a9dde3347e15a971c5da28df205496a217

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

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 21ed122c3a750ba711a08c78c8f2d54c3ef39d4c
Local-runs: none
Scope: packages/spec/src/api/documentation.zod.ts, packages/spec/src/api/plugin-rest-api.zod.ts, packages/spec/src/migrations/entries/retired-defs/18.api__ApiTestingUiConfig.ts, packages/spec/src/migrations/entries/retired-defs/18.api__ApiTestingUiType.ts, packages/spec/src/migrations/entries/retired-keys/18.api__ApiDocumentationConfig__ui.ts, packages/spec/src/migrations/entries/retired-keys/18.api__OpenApiGenerationConfig__uiFramework.ts, packages/spec/src/migrations/entries/retired-keys/18.api__OpenApiGenerationConfig__uiPath.ts, packages/spec/src/migrations/entries/semantic/18.api-docs-viewer-keys-retired.ts, packages/spec/src/migrations/registry.ts.template + packages/spec/src/shared/retired-key.ts, packages/spec/src/shared/retired-key-migrate-sentence.test.ts, packages/spec/src/migrations/entries/README.md, packages/spec/scripts/build-migration-registry.ts, packages/spec/scripts/step18-rationale-merge.test.ts, packages/spec/src/migrations/entries/semantic/18.rest-api-config-dead-keys-retired.ts, packages/spec/src/api/rest-api-config-dead-keys-retirement.test.ts, packages/spec/src/api/rest-server.zod.ts, packages/rest/src/rest-server.ts, packages/rest/src/discovery-routes.ts, packages/rest/src/ui-routes.ts, scripts/check-adr-0087-registration.mjs, scripts/cross-package-test-inputs.mjs, scripts/ci/spec-shape-retirements.json, packages/spec/tsconfig.test.json, packages/spec/vitest.config.ts, packages/spec/scripts/lib/export-origins-testkit.ts, packages/spec/scripts/lib/repo-walk-testkit.ts, packages/spec/liveness/ (directory listing), .objectui-sha; narrowing face (every diff path outside the prose faces, 22 of the 26): docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md, packages/spec/REST_API_PLUGIN.md, packages/spec/api-surface/api.json, packages/spec/authorable-defaults/api.json, packages/spec/authorable-surface/api.json, packages/spec/declaration-map/api.json, packages/spec/export-origins/api.json, packages/spec/json-schema.manifest/api.json, packages/spec/src/api/docs-viewer-keys-retirement.test.ts, packages/spec/src/api/documentation.test.ts, packages/spec/src/api/plugin-rest-api.test.ts, packages/spec/src/type-alias-convention.pin.test.ts, packages/spec/vitest.repo-tests.json, and the nine hits above. Prose faces read for ② only: .changeset/25159-api-docs-viewer-keys-retired.md, content/docs/references/api/documentation.mdx, content/docs/references/api/plugin-rest-api.mdx, content/docs/references/index.mdx.

Inputs: card #25159 (body and all three comments: triage 6332269878, claim 6333051310, dev report 6334544929), PR #25180 (body, 26-file list, net diff against main at the head), the check-runs on 21ed122c3. Every code claim below was read at origin/main 72a302a9d (the PR's base) with git show / git grep, plus the head's own hunks; the objectui census is a git grep in the sibling checkout at the pinned sha. Nothing was built, run or re-run.

① Derived judgments

  1. ApiDocumentationConfig.ui becomes a retiredKey() tombstone — right. The carrier is a non-strict z.object inside lazySchema, so the kit's route-2 rule applies (a bare deletion would strip in silence, ADR-0104). Accept-set after the change: the key is refused with invalid_type at path ui for every former value, the empty block included; every other key parses as before (the pin's CONTROL case checks enabled still defaults). The prescription follows the five house conventions as far as they apply: backticked fully-qualified key first, was removed in @objectstack/spec 18.1.0 (ADR-0049 enforce-or-remove), the dash clause saying why it was inert, Delete the key. plus the mechanism that is live. The os migrate meta sentence is correctly absent: retired-key.ts reserves it for a prescription whose surface an ADR-0087 conversion covers and requires it to be true of the tool, and retired-key-migrate-sentence.test.ts judges only sentences that are present. No tracker number in any prescription (the #25159 references sit in code comments, where check:doc-authoring allows them).

  2. RestApiPluginConfig.openApi.uiPath and uiFramework become tombstones in place of defaulted keys — right. Both lived on the plain z.object OpenApiGenerationConfigSchema, which RestApiPluginConfigSchema.openApi embeds as .optional() (line 713 at base), so the whole-config door refuses them at openApi.uiPath / openApi.uiFramework, which the pin asserts. The old defaults (/api/docs, swagger-ui) are no longer materialised (parse({}) grows neither key) and are refused like any other value. acceptRetiredDefaultResidue is not owed: its population is every artifact a released toolchain built with the default materialised (marketplace packages, installed environments). At the base, nothing outside packages/spec parses OpenApiGenerationConfigSchema or RestApiPluginConfigSchema; the only identifier hits are the two schemas' own unit tests, the handler-status pin's survivor-name list and one D3 entry's prose, and the REST plugin's runtime config is the TS interface in packages/rest, never this schema. The sibling rest-api-config-dead-keys-retired took the same disposition on defaulted members without the residue helper. The uiPath name outside spec is a local variable in packages/rest/src/ui-routes.ts:79 for the /ui/view route, unrelated, exactly as the card and the pin's structural-leg comment say.

  3. The orphan value defs api/ApiTestingUiConfig and api/ApiTestingUiType leave whole — right, and it is a public-surface removal. ApiTestingUiConfigSchema, ApiTestingUiConfig, ApiTestingUiConfigParsed and ApiTestingUiType (const and type) are removed from @objectstack/spec/api and, through the export * barrel, from the root. The census at the base finds no consumer besides ui (kit §4, orphan value schema). Both carry RETIRED_DEFS_BY_MAJOR[18] entry files; the template already has the retired-def:18 region (line 3755), and build-migration-registry.ts refuses an entry whose major has no region, so neither can be dropped silently. api-surface/api.json moves by exactly those five rows, which is the whole-def signature the kit predicts (and the reason the json-schema.manifest ratchet fired first).

  4. ADR-0087 registration — right. Three retired-keys/18.api__Def__key.ts files named per entries/README.md, each export const entry = 'api/Def:key' spelled byte-identically to the authorable-surface/api.json row it tombstones (gate b's exact-set read). One D3 semantic entry, api-docs-viewer-keys-retired, kebab-case, surface without backticks, reason and acceptanceCriteria non-empty; its own family rather than a member of rest-api-config-dead-keys-retired is right — different schemas (ApiDocumentationConfigSchema, OpenApiGenerationConfigSchema vs RestApiConfigSchema), different ruling (the viewer taken offline vs the rest_api liveness census), and the landed entry is untouched. No D2 conversion is right: neither config is a stack collection member nor a sys_metadata row, so no conversion seam ever sees one (the disposition the rest-api-config and handlerStatus retirements took on these same files). The STEP18_RATIONALE fragment is inserted where its id sorts (between agent-structured-output-refused-members-retired and cbp-master-relation-tiers-retired) with order: 108, one more than the list's current maximum of 107, which is the list's own rule.

  5. Generated baselines match the source removal row for row — right. authorable-surface/api.json: three [RETIRED] rows and the twelve api/ApiTestingUiConfig:* rows gone; authorable-defaults minus 10 (eight ApiTestingUiConfig defaults, uiFramework, uiPath); api-surface minus 5; declaration-map minus 3; export-origins minus 4; json-schema.manifest minus 2; references/index.mdx 1544 to 1542; the strictness-ledger count 448 to 446 (the two z.object sites inside ApiTestingUiConfigSchema). Type Check · source gates (spec tsc, check:spec-changes, check:upgrade-guide, check:export-origins, check:authorable-surface, check:docs, check:generated --reconcile-only) is green on the head.

  6. The prescription's facts are true of the runtime — right, and the card's /openapi.json is refined, not contradicted. packages/rest/src/rest-server.ts getApiBasePath() is api.apiPath ?? basePath/version; rest-server.zod.ts defaults basePath to /api and version to v1; registerRoutes() calls registerOpenApiEndpoints(bp) only while api.enableOpenApi (default true) is on; the body in discovery-routes.ts serves openapi.json under that base. No HTML viewer route survives in packages/rest/src (zero non-test hits for swagger, redoc or a /docs path), so "the platform serves no HTML API documentation viewer" holds at the base.

  7. Version string 18.1.0 — right. packages/spec is 18.0.0 at the base, the tag @objectstack/spec@18.0.0 exists, the changeset is minor, and the two sibling tombstones already landed on the 18 line (ui/view.zod.ts) say 18.1.0.

  8. Tests and their gates — right. The new pin asserts code, path and the prescription at each door, the old defaults included; the tsc channel with three @ts-expect-error marks in a file tsconfig.test.json compiles (its include covers src/**; check:test-typecheck runs inside Type Check · workspace's turbo run typecheck); zero holders for the four retired names with an anti-vacuity floor and named survivors; the registration; and a tree-scoped absence walk whose five roots are exactly the @objectstack/spec radius declared in scripts/cross-package-test-inputs.mjs (packages/**, content/docs/references/** plus the named docs page, scripts/**, skills/**, the examples non-code extensions), spelled as the sibling rest-api-config-dead-keys-retirement.test.ts spells it, with its two structural exclusions reasoned in place and no allowlist file. It is routed into the repo vitest project through vitest.repo-tests.json, which Test Core runs (turbo run test test:repo); check:cross-package-test-inputs (Lint · CI, release & engine gates) is green. The edited tests drop only authorings of the retired keys; type-alias-convention.pin.test.ts moves 789 to 788 with the history line. Reverse verification on uiPath (predicted red, observed 6 of 22 red, blob restored) and the two walk probes are reported with their readings on the PR body.

  9. Hand-written surfaces — right. REST_API_PLUGIN.md drops uiPath / uiFramework from the example and the "Multiple UI frameworks" bullet, and names the served document. At the base, no form, i18n bundle, published skill, example app or content/docs/** page outside the auto-generated references authors or imports any of the retired names (the census above), so no other hand-written edit is owed. The references pages are generator output.

  10. objectui and the Spec Shape Gate — right. Re-measured in the sibling checkout at the pinned .objectui-sha 56cf4d8e89b71586e4d9bb9921433d44b03addf3: 0 files for ApiTestingUi|uiPath|uiFramework|documentation\.ui|ApiDocumentationConfig|OpenApiGenerationConfig|enableTryItOut, control @objectstack/spec/api 103 occurrences. No scripts/ci/spec-shape-retirements.json entry is owed: its $comment ties an entry to a downstream objectui card, and there is nothing downstream to break.

  11. Liveness ledger not touched — right. No packages/spec/liveness/*.json and nothing under scripts/liveness names ApiDocumentationConfig, OpenApiGenerationConfig or RestApiPluginConfig, so there is no row to keep dead; Spec property liveness is green on the head.

  12. Scope of the narrowing — right. Three keys refused, four exports removed, nothing else moves: every other key of the three schemas keeps its parse and defaults (pinned), and the mounted REST surface never read any of the three.

Check-runs on 21ed122c3 (their conclusions are the gate verdicts). First read 06:46Z: 25 success, 6 skipped (the path-filtered full-battery lanes: Console Pin Gate, Dogfood Verify CLI, the sharded Dogfood Regression Gate, Temporal Conformance, and the two opt-in harnesses), 9 in_progress. Second read 06:53Z: Build Core, Build Docs, Type Check · consumer gates and Type Check · debt ledger had completed success; Test Core (1/6 to 6/6) and Type Check · workspace still in_progress. Final read 07:15Z, before posting: 42 check-runs, 36 success, 6 skipped, 0 in progress — Test Core 1/6 to 6/6 and the Test Core aggregate success (last shard 07:14Z), Type Check · workspace and the TypeScript Type Check aggregate success (07:00Z). All five required contexts are success: Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard. No governed surface is in the file list (the queue guard agrees); head repo equals base repo; 1,325 changed lines.

② Semver level

  • Changeset .changeset/25159-api-docs-viewer-keys-retired.md: '@objectstack/spec': minor, Clause-②: no (narrowing), Metadata-protocol: narrowed, the ADR-0087 marker registered api-docs-viewer-keys-retired, the BREAKING banner, a FROM → TO table and the one-line fix. The PR body carries the same two declaration lines. All three match what the diff publishes: an accept-set narrowing on a published authoring surface (three keys refused) plus four exports removed from @objectstack/spec and @objectstack/spec/api, both carried by the banner and the table; a narrowed kind takes at least minor on the package the diff moves and major is refused in the launch window (check-changeset-no-major), so minor is the only correct level. The marker resolves: check-adr-0087-registration.mjs reads entries/semantic among its registries, and the id names an entry that is NEW in this diff. Check Changeset is green. Only @objectstack/spec publishes anything the diff touches (the references pages are generator output, the audit ledger count is a repo file); no content/docs/releases/ edit. The "Deployed configuration was not measured" line is an honest bound, not a gap.

③ Boundary flags

Every dev flag from report 6334544929 and the PR body, answered:

  1. In-place retirement of uiFramework (not named on the card). Judged against the four conditions the review checklist sets for an adjacent fix: same defect class (a viewer selector for a viewer the platform does not serve, one line below uiPath) — holds; mechanical (same tombstone shape, same gates) — holds; no one else's claim — holds: I read the file lists of all ten other open PRs (the Version Packages PR by its first page), none touches plugin-rest-api.zod.ts, documentation.zod.ts or an 18.api__* entry; same gate family — holds. The PR body names it with evidence. The claim's file surface is unchanged (the file was already claimed); the key set grew by one, so the seat's maintainer report for this round lists three narrowed keys, as the dev asked. Answered, no rework.
  2. Files outside the claim surface (docs/audits/...counts/api.md, type-alias-convention.pin.test.ts, vitest.repo-tests.json): each is a mechanical consequence of the removal, verified in ① 5 and ① 8. Answered.
  3. Items of the suggested route measured not owed (no D2 conversion, no liveness rows, no spec-shape-retirements.json entry): each verified in ① 4, 10 and 11. Answered.
  4. Sequencing error during the local gate re-run: local only; no verdict here rests on a local run, the head's check-runs are the verdicts. Answered.
  5. Commit trailers: the seven branch commits carry Claude-Session plus Co-authored-by: Claude, model-free; the landing is a squash. Answered.
  6. Branch history of eight commits including a merge of origin/main: squash landing; the merged main moved no packages/spec path. Answered.
  7. open_questions: none.
  8. Out-of-scope finding, noted not filed: OpenApiGenerationConfigSchema and RestApiPluginConfigSchema as a whole, and the remaining keys of ApiDocumentationConfigSchema, are parsed by nothing outside their own unit tests, and openApi.outputPath defaults to /api/docs/openapi.json while the document is served at the API base. The dev's disposition follows the executor rule (dormant and zero-pull are not a filable class, and no producer is named). ESCALATED to the seat, not a fail: this is the same declared-but-unenforced family spec(api): after #25104 retires the docs viewer, api.documentation.ui (ApiTestingUiConfigSchema) and the REST API plugin's uiPath are declared and read by nothing; retire them (ADR-0049 enforce-or-remove) #25159 itself came out of (spec(api): after #25104 retires GET {base}/docs, rest-server.zod.ts still names the docs viewer (enableOpenApi's TSDoc and describe, and the documentation.enabled tombstone prescription) #25121's pointer), so whether it becomes a family close-out item is the seat's call.

Implemented-by: claude/issue-25159-api-docs-viewer-keys-retired
Reviewed-by: session_01T1Y1g92DfSEz93iuCisju4

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:spec seat 1 (seat post #6017) · os-bill · session session_01T1Y1g92DfSEz93iuCisju4 · 2026-10-22T07:17Z · reviewed head:

21ed122c3a750ba711a08c78c8f2d54c3ef39d4c
  • Form: draft, base main, first line Fixes #25159, and Clause-②: no (narrowing) and Metadata-protocol: narrowed line-initial. The only closing keyword in the body names spec(api): after #25104 retires the docs viewer, api.documentation.ui (ApiTestingUiConfigSchema) and the REST API plugin's uiPath are declared and read by nothing; retire them (ADR-0049 enforce-or-remove) #25159. Assignee os-bill. mergeable_state clean. Head repo is the base repo.
  • Scope: 26 files, 1325 changed lines (≤ 3000). check-governed-merges.mjs --pr 25180: 0 of 26 paths governed. No content/docs/releases/. Three files sit outside the claim's listed surface and are mechanical consequences: docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md (generator-owned), the ADR-0122 isomorphic-pin count in packages/spec/src/type-alias-convention.pin.test.ts, and packages/spec/vitest.repo-tests.json (the new pin's repo-project roster).
  • Contract review of record: 6335062098 on this head, Served-tier: CONTRACT_REVIEW_TIER, Local-runs: none, PASS. It covers the schema and narrowing faces, the whole non-prose diff.
  • Seat spot checks on origin/main:
    • openApi.uiFramework, retired beside uiPath under the in-place exemption, is read only by plugin-rest-api.test.ts and REST_API_PLUGIN.md. Its four conditions hold, and the PR body names it. This is a third narrowed key, and the round report carries all three.
    • Neither ApiDocumentationConfigSchema nor RestApiPluginConfigSchema / OpenApiGenerationConfigSchema is referenced outside its own files and tests in packages/spec/src, so no D2 conversion is owed.
    • No packages/spec/liveness/*.json row walks either schema.
    • objectui origin/main has 0 hits for ApiTestingUi, uiPath, uiFramework, ApiDocumentationConfig and OpenApiGenerationConfig, so no Spec Shape Gate registry entry is owed (its $comment ties an entry to a filed objectui card).
    • 18.1.0 in the prescriptions follows packages/spec/package.json 18.0.0 plus this minor, and matches the sibling tombstone in ui/view.zod.ts.
  • Changeset: @objectstack/spec minor, with both declaration lines, the BREAKING banner, FROM → TO, the one-line fix and the ADR-0087 registered marker. Prose checked against the diff: "Three keys declared an HTML API documentation viewer, and nothing ever read any of them" and "The mounted REST surface is unchanged" both hold per the spot checks above.
  • Gates (pinned to the head above): every check-run completed, 0 non-green. Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard and the aggregate Dogfood Regression Gate succeeded. check-expected-skips.mjs --pr 25180: "OK — 6 skipped check-run(s), every one in main's roster". The Docs Drift Check's six hand-written pages are matched through the unrelated uiPath local in packages/rest/src/ui-routes.ts (the /ui/view route), not through the retired schema key; there is nothing to re-verify there.
  • Dogfood battery (narrowing arm), on this head: "Test Files 287 passed and 3 skipped (290)", "Tests 2395 passed and 18 skipped (2413)", no failures.
  • Out of scope: the dev's Acceptance note (the rest of ApiDocumentationConfigSchema, and OpenApiGenerationConfigSchema / RestApiPluginConfigSchema as wholes, are parsed by nothing outside their own unit tests) is below the filing gate: no producer and no door reach. It is recorded on seat post [PM seat] domain:spec — ⏳ vacant #6017 as an enforce-or-remove pointer for the lane. Nothing filed.
  • Report: os-dev-report 6334544929 on spec(api): after #25104 retires the docs viewer, api.documentation.ui (ApiTestingUiConfigSchema) and the REST API plugin's uiPath are declared and read by nothing; retire them (ADR-0049 enforce-or-remove) #25159. mcp_calls 0. api_writes 2 relay strokes, both read back.
  • Landing: ordinary queue landing (ready, then auto-merge).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 22, 2026 07:18
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 22, 2026 07:18
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 39739741966 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test Core (3/6) — 失败步骤: Run this shard's tests

    create-objectstack:test:  FAIL  src/banner-version.test.ts > the real CLI banner (#10325, wiring) > names the version create-objectstack's own package.json actually declares
      ↳ 失败原因: create-objectstack:test: Error: Test timed out in 20000ms.
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

  • src/banner-version.test.ts — 24h 窗口内只有本 PR 撞到过,暂不汇总(再有一个不同 PR 撞到就会自动开汇总 issue)。
  • ⚠️ 24h 评论账本没读完(超过 5 页仍未读到窗口尽头),所以上面的「不同 PR 数」是下界,不是全量。

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 0 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue ejection · domain:spec seat 1 · session session_01T1Y1g92DfSEz93iuCisju4 · 2026-10-22T07:55Z

  • Signature (new: the triage comment 6335689882 counts it once in 24 h): queue run 39739741966, Test Core (3/6), step "Run this shard's tests": create-objectstack:test: FAIL src/banner-version.test.ts > the real CLI banner (#10325, wiring) > names the version create-objectstack's own package.json actually declares — Error: Test timed out in 20000ms.
  • First judgment: not this PR's. The three facts that allow one re-queue all hold:
    1. The failing file's import closure is disjoint from this diff. This PR changes packages/spec and docs only. banner-version.test.ts imports ./banner.js and node builtins and spawns the package's own CLI. packages/create-objectstack/src imports @objectstack/spec only in scaffold template files (src/templates/blank/**), which a version banner never loads.
    2. The queue base is green on the same shard. Base f7d740376d (fix(platform-objects): distinct zh-CN/ja-JP labels for the two Setup organization nav entries #25177's queue build, run 39739595869): Test Core (3/6) success, every Test Core job success.
    3. The first error is a timeout, not an assertion: "Test timed out in 20000ms".
  • Act: auto-merge re-armed once on the unchanged head 21ed122c3a. A second ejection on this signature is not re-queued; it goes to the next seat to re-diagnose, per the landing rules.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 14e7239 Oct 22, 2026
43 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25159-api-docs-viewer-keys-retired branch October 22, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

2 participants