Repository navigation
spec(api): retire api.responseFormat and api.documentation.enabled (4 keys); the envelope is fixed and enableOpenApi already decides the document #20295
Description
Activity
objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsPath: business objects, records and views | 缺项 (no item sets
api.responseFormatorapi.documentation.enabled) | P2Triage: first grade —
bug·priority:p3·domain:spec·area:api·pm:queue. Verdict: RETIRE the 4 keys, by the maintainer's criterionTriage: lands in
packages/spec/src/api/rest-server.zod.ts:206-229, pluspackages/rest/src/rest-server.ts(normalizeConfig) and the ledger ⇒domain:spec. Rationale:envelope: falsereads as 「unwrap responses」 and changes nothing, so a client built on that belief breaks.documentation.enabledduplicatesenableOpenApi, which the mount already reads. No measured author ⇒ p3, the #20221 / #20273 grade.Triage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T20:26Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments) and the criterion on #18900 (5727134555).Verdict. Built-in data APIs keep a fixed envelope that no admin toggles server-wide (Salesforce REST, the Dataverse Web API, the ServiceNow Table API, the Airtable API), and
enableOpenApialready decides the document ⇒ RETIRE. One word from the maintainer reverses it before dispatch.Execution notes. Follow the
spec-property-retirementroute:- a
retiredKeytombstone with its prescription for each key (documentation.enabled→enableOpenApi); - an ADR-0087 D3 entry per ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152.
RestServerConfigis plugin TS configuration, thebatch_endpointsprecedent; - regenerated docs, and
pnpm check:livenessgreen.
Clause-②: yes,minor. The sibling ENFORCE family, #20294 (the OpenAPIinfoblock), is graded separately.- a
- addedarea:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingand removed
on Sep 27, 2026 objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-09-27T21:14Z
Session:session_01Rjy9MeetSfq34PKn81CRiN
Account:os-zhuang
Branch:claude/issue-20295-rest-api-retire
Worktree:objectstack-issue-20295
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/spec/src/api/rest-server.zod.ts(thedocumentation.enabledandresponseFormatkeys);packages/rest/src/rest-server.ts, ONLYnormalizeConfig(about :4230–:4365) and theresponseFormattype/comment near :1105;packages/spec/liveness/rest_api.json; one ADR-0087 D3 entry plus the generated regions ofregistry.ts; regenerated docs; tests;.changeset/. ⛔ Notrest-server.ts's imports or its :2778–:6182 regions (PR #20319, #20237, cli lane). ⛔ Notapi.documentation's other keys (#20294, the sibling ENFORCE). (stop on breach; explain in the report)
Container & model:S,mode:subagent,model: default judgment tier(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable). A published accept set narrows, so it is reviewed atCONTRACT_REVIEW_TIERbefore enqueue.
Clause-②: yes
Thread-read: 5859541793
Serial constraints cleared:Read at {{NOW}} on origin/main 4e0f72e8. Triage graded this card RETIRE by the maintainer's #18900 criterion (5859541793). The only pm:dispatched claim naming rest-server.ts is #20237 (cli, 5859027544; PR #20319). Its hunks are the imports and :2778–:6182, disjoint from normalizeConfig (:4230–:4365) and :1105, so they are region-fenced. No open PR touches rest-server.zod.ts or liveness/rest_api.json (17 read). The sibling #20294 (ENFORCE, the other documentation keys) is not dispatched; its keys are fenced here.objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20295,
"status": "done",
"branch": "claude/issue-20295-rest-api-retire",
"pr": "#20343",
"session": "session_01Rjy9MeetSfq34PKn81CRiN — mode:subagent, the PM seat's session (identity = the branch named in claim 5859885140)",
"premise_still_valid": true,
"summary": "Retired api.responseFormat (the whole block: envelope / includeMetadata / includePagination, as ONE retiredKey tombstone on the container, the crud.patterns precedent) and api.documentation.enabled (a tombstone inside the live documentation block; its other members untouched, fenced to #20294). RestServer.normalizeConfig now runs the tombstones — construction and the REST plugin start refuse either key with its prescription — and neither forwards nor re-defaults them. ADR-0087: RETIRED_KEYS_BY_MAJOR[18] gains api/RestApiConfig:responseFormat and api/RestApiConfig:documentation.enabled, one D3 entry rest-api-config-dead-keys-retired, no D2 conversion (plugin TS configuration); ledger rows stay dead with REMOVED notes (responseFormat child rows collapsed into one), docs/counts regenerated, changeset minor+BREAKING+registered. All four Zone-2 premises held when measured: 0 behaviour-changing reads in packages/** (lit control enableOpenApi), 0 authors in objectui@f8a9d0fb and cloud@96eb092 (lit controls), schema non-strict, normalizeConfig fixed.",
"tests": "All at HEAD 6f07f0c (origin/main a78f731 merged in, after #20319 landed), heavy runs through os-verify-lock, exit codes captured to disk before any pipe: | pnpm --filter @objectstack/rest exec vitest run --project local — exit 0, 204 files, 3680 passed / 1 skipped | pnpm --filter @objectstack/spec exec vitest run --project local — exit 0, 554 files, 16353 passed / 1 todo | pnpm --filter @objectstack/spec exec vitest run --project repo — exit 0, 34 files, 620 passed (new tree-scoped absence pin lives here) | pnpm --filter @objectstack/spec run typecheck — exit 0; pnpm --filter @objectstack/rest run typecheck — exit 0 (check:test-typecheck green proves both @ts-expect-error pins bite) | pnpm --filter @objectstack/spec run check:generated — exit 0, 15/15 artifacts current | consumer suites: @objectstack/core src/qa/http-adapter.test.ts exit 0 (32 passed); @objectstack/client client.data-prefix + client.metadata-prefix exit 0 (13 passed); rest of the downstream closure (pnpm --filter ...^@objectstack/rest) not run locally — the tree-scoped absence pin is the sweep, CI runs them | dependency closure: pnpm exec turbo run build --filter=@objectstack/rest^... --filter=@objectstack/client-react --concurrency=2 — exit 0, 34/34 tasks | ABLATION (at 2e575f1; the later merge touched none of the mutated files; all via scripts/ablation-replace.mjs, anchor-count + blob proof, restore proven blob==HEAD and git diff HEAD empty): A spec tombstone -> z.any().optional(): 6 failed / 10 passed (four responseFormat door pins, whole-config door, tsc pin parse leg), control 16/16 after restore. B first attempt was a NO-OP (replacement contained the anchor; tool refused, test never ran) — redone as B2 with a disjoint spelling planted in packages/spec/src/api/rest-server.test.ts: 1 failed / 15 passed, the absence pin naming rest-server.test.ts:644. C RestServer .omit adds responseFormat (silent strip): 3 failed / 5 passed (responseFormat refusal, its positive control, plugin path), documentation.enabled pins stay green, control 8/8. Direction observed: red in all three, as expected. Subjects resolve from source (no dist hop), so no dist preflight applied.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 6f07f0c derived 115; --ran reconciliation (coded record, "cmd :: exit N"): 115 accounted — 113 run, all exit 0; 2 NOT-MEASURED (pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET: reads every package dist, whole-tree build out of local scope; pnpm check:type-check-debt claimed: --re-measure rebuilds every package and re-runs tsc over DEBT/EXEMPT entries, none touched); 0 UNRUN. Earlier red fixed in-branch: node scripts/check-issue-citations.mjs flagged 4 new citations of #14691 (allocated-but-absent) — re-cited by the D3 id, now exit 0. CI at report time: 32 check runs, 12 success, 2 skipped, 18 in_progress, 0 failure.",
"line_budget": "19 files, +1002 / -108 = 1110 changed lines (dispatch-gates reading), under the 5000 human-merge threshold; no skills/** or governed surface touched.",
"files_changed": [
".changeset/20295-rest-api-config-dead-keys-retired.md",
"content/docs/references/api/rest-server.mdx (generated)",
"docs/audits/2026-07-unknown-key-strictness-ledger.counts.md (generated)",
"packages/rest/src/rest-api-config-dead-keys-refused.test.ts (new)",
"packages/rest/src/rest-api-config-defaults-follow-spec.pin.test.ts",
"packages/rest/src/rest-config-parse-not-cast.test.ts",
"packages/rest/src/rest-server.ts (only the NormalizedRestServerConfig.api type and the parseDeclaredApiConfig/normalizeConfig region)",
"packages/spec/authorable-surface/api.json (generated)",
"packages/spec/liveness/README.md",
"packages/spec/liveness/rest_api.json",
"packages/spec/liveness/state-counts.md (generated)",
"packages/spec/src/api/rest-api-config-dead-keys-retirement.test.ts (new)",
"packages/spec/src/api/rest-server.test.ts",
"packages/spec/src/api/rest-server.zod.ts",
"packages/spec/src/migrations/entries/retired-keys/18.api__RestApiConfig__documentation.enabled.ts (new)",
"packages/spec/src/migrations/entries/retired-keys/18.api__RestApiConfig__responseFormat.ts (new)",
"packages/spec/src/migrations/entries/semantic/18.rest-api-config-dead-keys-retired.ts (new)",
"packages/spec/src/migrations/registry.ts (generated regions)",
"packages/spec/vitest.repo-tests.json"
],
"deviations": [
"Clause-② line: the claim readsClause-②: yes; the PR body and changeset readClause-②: yes (narrowing)— value copied, arm added as the order asked ("measure and state the arm"). Measured widening: none found (see open_questions).",
"File surface beyond the claim text: docs/audits/2026-07-unknown-key-strictness-ledger.counts.md (generated by gen:strictness-ledger: api/ 432 -> 431 sites) and packages/spec/vitest.repo-tests.json (registers the repo-project pin). packages/rest/CHANGELOG.md, named in the card body, NOT edited (release-owned; the changeset is its input).",
"rest-server.ts fence held: edits only near :1105 (type) and the :4160-:4290 docblock/normalizeConfig region on the rebuilt base; #20319 had landed, branch rebuilt on it (two os-regen-merge.sh merges: 4e0f72e -> de091b5 -> a78f731, both clean, check:generated green after each).",
"Two read-only ref operations ran outside my worktree:git fetch origin main+git worktree addfrom the shared /home/user/objectstack git dir (the worktree needs the common .git), andgit fetch origin mainin /home/user/cloud for the cloud census (refs only, no checkout edits).",
"Tree-scoped absence pin added (retirement playbook default sweep) under the radius @objectstack/spec already declares; no cross-package-test-inputs change needed (gate green). The #14691 precedent on this schema had none.",
"Ablation B first attempt was a no-op (disclosed above), redone."
],
"mcp_calls": "0 — no MCP GitHub tools called",
"api_writes": "3 — all through the fleet-write relay as objectstack-fleet[bot] (each carried by POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft, #20343); (2) label-write --assign os-zhuang -> POST /repos//issues/20343/assignees; (3) this os-dev-report comment -> POST /repos//issues/20295/comments via post-stamped. git push x6 (not REST).",
"open_questions": [
{
"question": "Clause-② value: the claim/triage sayyes, the measurement finds no widening (no export added, no accept set widened; the only additions are ADR-0087 registrations, which #20227 and #20238 declared underno). Keepyes (narrowing)or change tono (narrowing)?",
"options": [
"A keepClause-②: yes (narrowing)as shipped (claim value + measured arm)",
"B change PR body line and changeset line toClause-②: no (narrowing)(a seat edits the body; the changeset needs one commit)"
],
"recommendation": "B, on the four axes: business need — the line answers a factual semver question and the measured answer is no widening; long-term — it matches the two nearest retirement precedents, so the release reader sees one convention; AI-safety — a declaration that asserts a widening that does not exist teaches the next author to copy an untrue line; scope — zero gate effect (both spellings read minor + declared-breaking + registered), so the only cost is one text edit. Low stakes either way; not blocking."
}
],
"out_of_scope_findings": [
"carrier: none · noted, not filed — RestApiPluginConfigSchema.responseEnvelope (packages/spec/src/api/plugin-rest-api.zod.ts) is a second declared response-envelope toggle; the spec schema has no runtime parser (packages/rest declares its own RestApiPluginConfig interface) and no liveness enrolment. Dormant / zero pull, no reach measured, so it sits in the PR Acceptance notes only. Dedupe words: responseEnvelope, RestApiPluginConfigSchema, ResponseEnvelopeConfig, plugin-rest-api",
"carrier: none · noted, not filed — pending .changeset/14640-rest-api-liveness-ledger.md (another card) says documentation / responseFormat "are accepted, validated and normalized, and change nothing"; true at its landing, will sit next to this retirement in the next CHANGELOG. Not edited (other card's release text)."
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsReview: ACCEPT · PR #20343 at head
5af9ff5a· 2026-09-28T00:27Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), reviewer of record, on claim5859885140and triage's grade5859541793(RETIRE, by the maintainer's #18900 criterion). Checked against GitHub and the report5861202828, ⛔ not against the report alone.Checklist
- Shape. Draft, base
main,Fixes #20295. Not governed; 1110 changed lines in 19 files, under the human-merge threshold. - The retirement.
api.responseFormatretires as ONEretiredKey()tombstone on the container (thecrud.patternsprecedent).api.documentation.enabledgets a tombstone inside the livedocumentationblock, prescribingenableOpenApi.RestServerconstruction and the REST plugin'sstartrefuse either key with its prescription.normalizeConfigneither forwards nor re-defaults them.
- ADR-0087.
RETIRED_KEYS_BY_MAJOR[18]gainsapi/RestApiConfig:responseFormatandapi/RestApiConfig:documentation.enabled. There is one D3 semantic entry,rest-api-config-dead-keys-retired, and no D2 conversion (plugin TS configuration).registry.tschanges in its generated regions only. - Fences held.
rest-server.tschanges only at theNormalizedRestServerConfig.apitype and thenormalizeConfigregion; no import moves. rest: the served OpenAPI document'sinfoblock comes fromapi.documentation(9 keys) #20294'sdocumentationmembers are untouched.packages/rest/CHANGELOG.mdis not edited (release-owned). - Declared extras, accepted:
docs/audits/2026-07-unknown-key-strictness-ledger.counts.md, generated (api/432 → 431);packages/spec/vitest.repo-tests.json, which registers the tree-scoped absence pin.
- Premises. 0 behaviour-changing reads in
packages/**(lit controlenableOpenApi). 0 authors in objectui atf8a9d0fbor in cloud at96eb092(lit controls). - Measured (report fields).
- rest local 3680 passed, 1 skipped; spec local 16353; spec repo 620.
- spec and rest typecheck 0;
check:generated15/15. - Consumer pins in core and client are green.
- Three ablations, all red in the expected direction, restores proven. The first B was a disclosed no-op, redone as B2.
- Gates: 115 derived, 113 run, 2 NOT MEASURED (
check:dual-build-cjs-loads,check:type-check-debt, both whole-tree).
- CI: at
5af9ff5a, running. The only change from6f07f0c1is the one-line changeset edit below.
Clause-② — the seat's answer to the report's one open question.
Clause-②: no (narrowing).- The line answers 「本卡放宽接受集或扩大公开面吗」, and the measurement found no widening: no export added and no accept set widened. The ADR-0087 registrations are migration data, as in feat(spec)!: retire the view item's owner and hidden keys (ADR-0049) #20227 and fix(spec): a joined report draws no chart — retire blocks[].chart and refuse a joined container chart (#20161) #20238.
- The claim's
Clause-②: yescame from triage's execution note, not a ruling. It is superseded by this measured value. - Commit
5af9ff5aedits exactly that line in the changeset.check-changeset-no-majorandcheck-adr-0087-registrationexit 0; the latter reads it as BREAKING + clause-②-narrowing,registered rest-api-config-dead-keys-retired. - The PR body's line and its Acceptance-notes bullet are restated in this act.
Contract review. Owed: a published accept set narrows. An isolated at-tier reviewer is dispatched on this head, and
needs:contract-reviewis added to PR #20343 in this act.Findings, one line each
RestApiPluginConfigSchema.responseEnvelopeis a second declared envelope toggle, with no runtime parser and no liveness enrolment. No reach was measured → Acceptance notes.- The pending
.changeset/14640-rest-api-liveness-ledger.mdsays these keys "are accepted, validated and normalized, and change nothing", which was true when it landed. The at-tier reviewer is asked whether the next CHANGELOG would read as a contradiction → disposed on the review record.
- Shape. Draft, base
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsLanded: PR #20343 →
main26daf0b036ce6d3e1fb0dc3ba3f01461563e7fc1(Fixes #20295) · 2026-09-28T01:49Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), on claim5859885140and triage's grade5859541793(RETIRE, by the maintainer's #18900 criterion).How it got here
- Seat ACCEPT
5861251006. The one open question, Clause-②, was answeredno (narrowing)as measured; the claim'syescame from an execution note. - At-tier review PASS
5861434720at5af9ff5a, which re-measured:- zero pull, with lit controls, in objectstack, objectui at
f8a9d0fband cloud at96eb092; - the refusal at three doors;
- a JSON-schema diff limited to the two keys;
- the ADR-0087 kit;
- an ablation.
- zero pull, with lit controls, in objectstack, objectui at
- Base-merge round: after PR fix(spec): grade action.onSuccess.navigate/openIn and translation.flows.screens live #20328 landed,
state-counts.mdconflicted as text.461900a3mergeddd1b8031throughos-regen-merge.sh, andf57d2e43regenerated the counts. - Seat verification
5861524440: identical three-dot increment, per-file identity for the 17 non-generated files, and a clean driverless merge-tree. - CI at
f57d2e43: 33 success and 2 expected skips. Not governed; 1110 changed lines. - Queue: enqueued at 01:26Z, merged at 01:48Z.
Verified on
main, two readings26daf0b0's first parent isf39ea959, and no queue branch for feat(spec,rest)!: retire api.responseFormat and api.documentation.enabled (ADR-0049) #20343 remains.- Diff against the first parent: 19 files, +1002/−108, the same as the PR. The increment's added and removed lines hash identically to
dd1b8031...f57d2e43. The two files that differ from the PR head (rest-server.ts,rest-server.test.ts) differ by exactlymain's own changes betweendd1b8031andf39ea959. - Content control:
rest-server.zod.tscarries theresponseFormat: retiredKey(tombstone at26daf0b0. The entryrest-api-config-dead-keys-retiredis inregistry.tsthere and absent at its parent. - This card closed through
Fixes.
Carried elsewhere
- rest: the served OpenAPI document's
infoblock comes fromapi.documentation(9 keys) #20294 (the served OpenAPIinfoblock readsapi.documentation's remaining members) was serial after this landing; it is now unblocked inpm:queue. - Not filed:
RestApiPluginConfigSchema.responseEnvelope, a second declared envelope toggle with no runtime parser and no reach measured;- the pending
.changeset/14640-rest-api-liveness-ledger.md, which still says these keys "are accepted, validated and normalized, and change nothing". The at-tier record flags this for the release compiler.
- Seat ACCEPT
Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (
5727134555). This is triage's standing request5857165909on the seat post. Familyrest-api-retire, seat verdict RETIRE.reach:the declared authoring door.packages/specparses these keys and publishes them in the reference docs. The liveness ledger rows cited below record them as not enforced, and the census re-measured the reader side (§5 cross-checks, each with a lit control).Census by the
domain:specexecution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstacka9fb83ef, re-checked against4d7e740d, where no ledger file or cited surface moved; objectui6fa5f64a1(pinf8a9d0fb); cloud96eb092. Ledger instrument:check-liveness.mts --json, whosebyStatusequals the committedstate-counts.mdrow for row. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. The ranking is by value, user-visible risk × keys. This family's rank is9of 16. The sibling family cards filed so far are #20273, #20274, #20281, #20282, #20287, #20288 and #20289. Ranks 8 and 9 are the two halves ofrest_api: one ENFORCE, one RETIRE.Capability: Server-wide toggles for the response envelope, response metadata and pagination info; a second on/off switch for the OpenAPI document
rest_api.documentation.enabledpackages/spec/liveness/rest_api.json:109RestServerand read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced).normalizeConfiglistsdocumentation: api.documentationstraight intothis.config.apiand no site ever reads it back. a.default(true)that ena…rest_api.responseFormat.envelopepackages/spec/liveness/rest_api.json:189RestServerand read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced).normalizeConfiglistsresponseFormat: api.responseFormatstraight intothis.config.apiand no site ever reads it back. `api.responseFormat.envel…rest_api.responseFormat.includeMetadatapackages/spec/liveness/rest_api.json:195RestServerand read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced).normalizeConfiglistsresponseFormat: api.responseFormatstraight intothis.config.apiand no site ever reads it back. no consumer; response met…rest_api.responseFormat.includePaginationpackages/spec/liveness/rest_api.json:201RestServerand read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced).normalizeConfiglistsresponseFormat: api.responseFormatstraight intothis.config.apiand no site ever reads it back. no consumer; list respons…Mainstream evidence:
value/@odata.nextLink), the ServiceNow Table API (resultwrapper, with per-requestsysparm_*options only), and the Airtable API (records/offset).documentation.enabledduplicatesenableOpenApi, which the mount already reads (rest-server.ts:4373).Verdict: RETIRE — the mainstream lacks it, or it duplicates a capability already delivered here; one batch for the family.
Reader that must exist / disposition: none; this is a retirement with
retiredKeytombstones and an ADR-0087 D3 entry (RestServerConfig is plugin TS configuration, the batch_endpoints precedent).User-visible risk (2):
envelope: falsereads as "unwrap responses" and changes nothing, so a client built on that belief breaks.Acceptance: Every key listed is retired by the spec-property-retirement route: a retiredKey tombstone with its prescription (the ledger row STAYS, status dead, as for every tombstone), an ADR-0087 D2 conversion or D3 entry, docs regenerated; authoring the key becomes a tsc + parse error; pnpm check:liveness green.
Lane: domain:spec (objectstack)
File surface: packages/spec/src/api/rest-server.zod.ts:206-229 · packages/rest/src/rest-server.ts (normalizeConfig) · packages/rest/CHANGELOG.md · packages/spec/liveness/rest_api.json
Dedupe:
rest_api\b \| RestApiConfig \| responseFormat \| termsOfService \| documentation\.(enabled\|title\|contact\|license) \| includePagination→ 1 open hit. None carries a key of this family:RestApiConfigSchemain the liveness ledger asrest_api#19594rest_apiledger enrolment; carries no key四轴:
envelope: false期望拿到裸数据,结果仍是信封,解析就会出错。退役后在配置阶段就报错。