Repository navigation
rest: the served OpenAPI document's info block comes from api.documentation (9 keys) #20294
Description
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 request 5857165909 on the seat post. Family rest-api-documentation, seat verdict ENFORCE.
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).- The criterion is the maintainer's: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」.
- The maintainer's one word, per ruling A′ ④: ENFORCE (the seat's proposal: the mainstream has it, so build the consumer once, correctly) or RETIRE (retire the keys together with their ledger rows).
Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstack a9fb83ef, re-checked against 4d7e740d, where no ledger file or cited surface moved; objectui 6fa5f64a1 (pin f8a9d0fb); cloud 96eb092. Ledger instrument: check-liveness.mts --json, whose byStatus equals the committed state-counts.md row 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 is 8 of 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 of rest_api: one ENFORCE, one RETIRE.
Capability: API documentation metadata (title, description, version, terms, contact, license) in the served API description
| key | ledger status | ledger row | what the ledger cites |
|---|---|---|---|
rest_api.documentation.title |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:115 |
note: Normalized by RestServer and read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced). normalizeConfig lists documentation: api.documentation straight into this.config.api and no site ever reads it back. `api.documentation.title: '… |
rest_api.documentation.description |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:121 |
note: Normalized by RestServer and read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced). normalizeConfig lists documentation: api.documentation straight into this.config.api and no site ever reads it back. no consumer; the served `in… |
rest_api.documentation.version |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:127 |
note: Normalized by RestServer and read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced). normalizeConfig lists documentation: api.documentation straight into this.config.api and no site ever reads it back. no consumer; the served `in… |
rest_api.documentation.termsOfService |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:133 |
note: Normalized by RestServer and read by NOTHING — the ADR-0049 fourth state (parsed, unmarked, unenforced). normalizeConfig lists documentation: api.documentation straight into this.config.api and no site ever reads it back. no consumer, and the served… |
rest_api.documentation.contact.name |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:145 |
note: The contact name an OpenAPI info.contact would carry. No consumer: the served document's info.contact is the literal { name: 'ObjectStack', url: 'https://objectstack.io' } written by packages/spec/scripts/build-openapi.ts, and rest-server.ts#registerO… |
rest_api.documentation.contact.url |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:151 |
note: The contact URL an OpenAPI info.contact would carry. No consumer; the served value is the build-openapi.ts literal. Drilled rather than left riding on its container's blanket verdict, and NOT by fanning the parent's status out over its children — the call… |
rest_api.documentation.contact.email |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:157 |
note: The contact email an OpenAPI info.contact would carry. No consumer, and the served document carries no info.contact.email at all — build-openapi.ts writes only name and url. Drilled rather than left riding on its container's blanket verdict, and NOT… |
rest_api.documentation.license.name |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:171 |
note: The license identifier an OpenAPI info.license would carry. z.string(), no .optional()), so authoring a license object at all forces a value the runtime then ignores. No consu… |
rest_api.documentation.license.url |
dead (verified 2026-09-21) | packages/spec/liveness/rest_api.json:177 |
note: The license URL an OpenAPI info.license would carry. No consumer; the served value is the Apache-2.0 literal URL from build-openapi.ts. Drilled rather than left riding on its container's blanket verdict, and NOT by fanning the parent's status out over its… |
Mainstream evidence:
- The OpenAPI
infoobject is the standard carrier. Azure API Management and Apigee let the API owner set it. - ServiceNow Scripted REST APIs have a name, description and documentation per API, shown in the REST API Explorer.
- Power Platform custom connectors have a "General information" tab (description, icon, host).
- Salesforce and Dataverse publish fixed platform API docs with no admin-set info. The capability exists where the platform lets owners publish an API, which is this surface.
Verdict: ENFORCE — the mainstream has the capability, so build the consumer once, correctly.
Reader that must exist / disposition: objectstack packages/rest/src/rest-server.ts#registerOpenApiEndpoints (:4869) builds enriched from the bundled spec and overrides servers, paths and tags. It must also overlay enriched.info from this.config.api.documentation. Today normalizeConfig writes the key (:4252) and nothing reads it back.
User-visible risk (1): Developer-facing documentation text; the silent no-op is cosmetic.
Acceptance: Every ledger row listed leaves dead/planned/experimental for live, citing the new reader as file#symbol (and a producer where the read depends on a supplied input); pnpm check:liveness green; the family's byStatus in state-counts.md regenerated.
Lane: domain:spec parent + packages/rest sub-issue (objectstack)
File surface: packages/spec/src/api/rest-server.zod.ts:206-225 · packages/rest/src/rest-server.ts (normalizeConfig, registerOpenApiEndpoints) · 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:
- decision(pm): this seat dispatched 5 clause-② reviews at the RETIRED model tier after the maintainer ruling landed — two PRs merged on void verdicts #19603 — process decision about the feat(spec): enrol
RestApiConfigSchemain the liveness ledger asrest_api#19594rest_apiledger enrolment; carries no key
四轴:
- 实际业务需求: 对外发布 API 的团队需要在文档里写上标题、版本、联系人和许可。这是 OpenAPI 的标准字段。
- 项目长远合理性: 消费端只需在已有的 OpenAPI 组装函数里加一步覆盖,把声明与产物接起来。
- 防 AI 写错: 写进去却不出现在文档里,会让 AI 以为配置位置错了而反复改写。接上之后结果可见、可测。
- 创业阶段不扩散: 一个函数里十几行,零新机制。
Unblocked by ruling 5862477514 on #20359 (B, batch #231 item 1; note 5862495365) — was blocked by #20359 from 2026-09-28T02:45Z. Execution: eight keys ENFORCE + documentation.version RETIRE, per the ruling.
Activity
objectstack-fleet commentedon Sep 28, 2026
Transition:
needs-user-decision→pm:blocked·Blocked-by: #20359added to the body · 2026-09-28T02:45Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN). At the maintainer's request in chat (「看不懂,开决裁卡」), the question in theRelease:comment above now has its own decision card, #20359, written for a plain read. This card waits on it, and re-dispatches on its answer from the dev's kept resume plan (A), as eight ENFORCE plus one RETIRE (B), or as a RETIRE re-grade (C).objectstack-fleet commentedon Sep 28, 2026
Unblocked: #20359 ruled B (batch #231 item 1,
5862477514) ·pm:blocked→pm:queue· 2026-09-28T03:01ZDirector seat (objectstack#12708, summon #30 续 2,
session_01AsCNgFBs8HCjwhyHQsFbx3). The maintainer's word on #20359: 「同意 B」. This card'sBlocked-by: #20359is resolved by that ruling; the body line is replaced in the same stroke.Re-dispatch on the dev's kept worktree and resume plan as the seat's own B:
api.documentationkeys ENFORCE —title,description,termsOfService,contact.*,license.*: an authored value overlays the servedinfoon both doors; nothing authored ⇒ byte-identical to the artifact;titlebecomes.optional();contact/licensereplace whole.documentation.versionRETIRE —retiredKey()tombstone; the servedinfo.versionstaysSPEC_VERSIONper [finding] the served openapi.json overrides theinfo.versionthatpackages/specowns, so the published artifact says17.2.0and the served document saysv1#11646 (5405070350); ADR-0087 D3 entry.CONTRACT_REVIEW_TIER;@objectstack/specminor. The landing PR closes [Decision] May an app owner put their own title, description, version, contact and license on the API document ObjectStack serves (/openapi.json)? #20359 too.feat(spec,rest)!: retire api.responseFormat and api.documentation.ena…
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 3
Session:
session_01ARcDurZ5j34RdqsGgc4jgHAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-20294-openapi-info-overlayWorktree:
objectstack-issue-20294Domain:
domain:specSeat:
domain:spec#4(seat post #18917)File surface: a vertical dispatch (ruling A′ ② on #18900), with both ends declared. It executes ruling B on #20359 (
5862477514).packages/rest/src/rest-server.ts(domain:cli, the cross-lane surface): ONLY theregisterOpenApiEndpointsregion (a pure overlay helper plus the handler line that uses it, on both doors, and the [finding] the served openapi.json overrides theinfo.versionthatpackages/specowns, so the published artifact says17.2.0and the served document saysv1#11646 comment there). Plus its tests inpackages/rest/src/.packages/spec/src/api/rest-server.zod.ts:documentation.title→.optional(), aretiredKey()tombstone ondocumentation.version, and the describes of the eight enforced members. Plus regenerated JSON schema and reference docs.api.documentation.version, and whatever thespec-property-retirementroute requires with it: the retired-key entry,registry.tsand the step-18 lists, regenerated where a tool generates them. The precedent is spec(api): retireapi.responseFormatandapi.documentation.enabled(4 keys); the envelope is fixed andenableOpenApialready decides the document #20295'sdocumentation.enabled(PR feat(spec,rest)!: retire api.responseFormat and api.documentation.enabled (ADR-0049) #20343,26daf0b0).packages/spec/liveness/rest_api.json: the eight rows (and the two container rows) → live, and theversionrow retires with the key. Plusstate-counts.mdfromgen:liveness-counts, and the README row.'ObjectStack API'default flip. The [finding] the served openapi.json overrides theinfo.versionthatpackages/specowns, so the published artifact says17.2.0and the served document saysv1#11646 whole-block pin inrest-openapi-route.test.tsstays as the unset control..changeset/:@objectstack/specminor, and@objectstack/restper its bump policy.build-openapi.ts(the artifact stays the default source). ⛔api.versionand the runtime version never enterinfo.version. ⛔ No otherrest-server.tsregion. ⛔ Notpackages/runtime: [finding] the runtime dispatcher's/packagesdomain takes the organization from the raw session claim (resolveActiveOrganizationId, 9 sites), bypassing ruling B on #15409: a session naming a left organization may read and write it #20477's claim holdshttp-dispatcher.ts.(stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: default judgment tier(no path-derived mandate).Clause-②: yesper the ruling, so an at-tier contract review atCONTRACT_REVIEW_TIERis owed before enqueue.Clause-②: yes
Thread-read: 5862495365
Serial constraints cleared: read at 2026-09-28T18:45Z on
origin/mainfc0db22b.rest-server.ts,rest-server.zod.ts,rest_api.jsonorrest-openapi-route.test.ts. PR feat(spec)!: retire the inner name on cube measures and dimensions — the record key is the member's name (#20300) #20458 (spec(analytics): retire the innernameon cube measures and dimensions; the record key is the identity (2 keys) #20300, seat 1) editspackages/spec/liveness/README.md, and PRs feat(spec)!: retire the inner name on cube measures and dimensions — the record key is the member's name (#20300) #20458 / feat(spec,cli)!: one stack authoring shape — os validate / os build refuse a default export defineStack did not build #20460 / fix(spec): reword the CurrencyConfigSchema.precision clause in two major-18 D3 entries #20488 editmigrations/registry.ts. Both are ordinary concurrency: regenerate on merge, and edit the README's hand-written row after mergingmain./packagesdomain takes the organization from the raw session claim (resolveActiveOrganizationId, 9 sites), bypassing ruling B on #15409: a session naming a left organization may read and write it #20477 (domain:cli, p0) holdspackages/runtime/src/http-dispatcher.tsand readspackages/rest/**only.rest-server.tschange onmainis5c7aa467(fix(rest, runtime): the dispatcher's /meta doors scope to the vetted organization, and its item read, book tree and list answer what RestServer answers (#20408) #20473), in the/metadoors, a different region.area:api's other in-flight cards, objectql: refuse a non-numeric string compared against a number field at the engine's field-aware filter door (400, every driver and position) — the door half of #20336 #20351 (packages/objectql) and this seat's [finding] once PR #20218 lands,PackageInstallBodySchema's published docblock still says the install door answers 201 to residual classes 1b, 2, 3 and 4, which it then refuses 400 #20219 (package-api.zod.ts, prose), have disjoint file surfaces.5862210322) in seat 1's own container, which this seat cannot reach. The resume plan is carried as text, and the dev rebuilds fromorigin/main.Ruling carried:
5862477514· #20359 · batch #231 item 1 · letter B · maintainer 「同意 B」. The landing PR closes #20359 too, per that ruling.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHobjectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20294, "status": "done", "branch": "claude/issue-20294-openapi-info-overlay", "pr": "https://github.com/objectstack-ai/objectstack/pull/20512", "session": "session_01ARcDurZ5j34RdqsGgc4jgH — the PM's session (mode:subagent); this dev's identity is the branch named by claim 5876330236, the newest Claim on the card (verified before any edit)", "premise_still_valid": true, "summary": "Ruling B on #20359 executed on draft PR #20512 (head 69b4d203; origin/main 2b24b8b8 merged in through os-regen-merge.sh, no generated path moved). ENFORCE half: RestServer.overlayDocumentationInfo, a pure private static helper directly above registerOpenApiEndpoints, lays the authored title / description / termsOfService and a whole-replaced contact / license over the bundled info. It returns the bundled object itself when nothing is authored (byte-identical, the #11646 control kept unedited) and a NEW object otherwise, never writing into the cached artifact. One handler line applies it before res.json; the same closure serves both doors. RETIRE half: documentation.version is a retiredKey() tombstone whose prescription names the protocol version (spelled as the @objectstack/spec package version, since the in-repo PROTOCOL_VERSION constant 17.0.0 is a different value) and sends an app release number to description. It is registered in RETIRED_KEYS_BY_MAJOR[18] with its own D3 entry rest-api-documentation-version-retired and no D2 conversion. title is .optional(); the ledger moves rest_api from 12 live / 12 dead to 20 / 4; changeset spec+rest minor, Clause-②: yes (narrowing). Premise re-measured at fc0db22b before editing: 0 of 9 honoured on both doors, unset controls byte-identical, served info === cached artifact info. One thing needs the PM: line 2 Fixes #20359 (as ordered) turns the advisory check \"The card this PR closes must claim this branch\" red, because #20359 carries no Claim naming this branch and a dispatched dev may not post one (see open_questions).", "mechanism_assumptions": [ "1 HOLDS — measured at fc0db22b with a scratch vitest (deleted, never committed) driving both openapi.json handlers with project scoping on. All nine authored: served info equals the artifact on both doors, 0 of 9 honoured. Absent, {}, description-only and license-name-only: the served info equals the artifact, and the scoped door equals the unscoped one. sameRefAsCache was true in every case, so the overlay builds a new object; reverse-verified by leg B.", "2 HOLDS with one refinement. The route is retiredKey() on the non-strict inline documentation object, following the #20295 documentation.enabled precedent. It owes exactly: the RETIRED_KEYS_BY_MAJOR[18] exact-key entry api/RestApiConfig:documentation.version (retired-keys/18.api__RestApiConfig__documentation.version.ts), one D3 semantic entry per family rest-api-documentation-version-retired (semantic/18.rest-api-documentation-version-retired.ts), registry.ts regenerated by gen:migration-registry, no D2 conversion and so no MIGRATIONS_BY_MAJOR[18].conversionIds entry, and the ledger row kept dead with a REMOVED note. The Clause-② line takes the narrowing arm: Clause-②: yes (narrowing), where yes is the ruling's value and (narrowing) is required because a formerly accepted input is now refused (breakingDeclaration reads that arm). Refinement: the adr-0087 marker lives in the CHANGESET body, not the PR body. check-adr-0087-registration reads it there, as the HTML-comment marker registered rest-api-documentation-version-retired. The PR body carries only the Clause-② line and no marker, since HTML comments are banned on GitHub text.", "3 HOLDS. spec rest-api-config-dead-keys-retirement.test.ts (the documentation: {} control, was :172) now asserts {} with no title plus a description-only parse. rest rest-config-parse-not-cast.test.ts §D (was :352) now asserts no title and the block equal to exactly what was authored. Both flipped with the new substance asserted. The pins that authored documentation.version moved to the tombstone: the spec retirement CONTROL block and the two fixtures in packages/spec/src/api/rest-server.test.ts (:138 and the integration fixture). The tsc sweep found no other author, and the extended tree-scoped absence walk found none in packages/examples/skills/content/scripts.", "4 NEITHER is class a/b/c; this PR is not widened. (i) The /docs viewer HTML title literal ObjectStack API Docs is the Scalar page browser-tab title, not an info field. The page body renders the served (overlaid) info.title, no declared contract ties the tab to it and no key authors it, so it is polish. (ii) OpenApiGenerationConfigSchema.title third default: 0 non-test readers under packages/** (only migration-entry prose names the schema) against a lit control (RestApiConfigSchema found in rest-server.ts). No runtime parser, no ledger enrolment, no producer, so it is dormant. Both are in the PR Acceptance notes." ], "tests": "All at HEAD 69b4d203, heavy runs through os-verify-lock.sh, exit codes written to disk before any pipe. rest local: vitest run --project local exit 0, 219 files, 4185 passed, 34 skipped (VERDICT command-exit 0, held 172s). rest repo: exit 0, 1 file, 8 passed. spec local: exit 0, 572 files, 16794 passed, 1 todo (held 195s). spec repo (test:repo): exit 0, 38 files, 697 passed (held 583s; the tree-scoped absence walk runs here). rest typecheck and spec typecheck: both exit 0 (tsc --noEmit + check:test-typecheck; spec also check:scripts-typecheck). check:test-typecheck green proves the new @ts-expect-error pin bites. check:generated exit 0 (all 15 current); check:liveness exit 0. Narrowed eslint --no-inline-config --format json over the 11 changed code files: 11 files, 0 errors, 0 warnings. Population: eslint.config.mjs catch-all files glob; invariance: the config enables no type-aware linting. Pre-restart targeted run at 38a73bb4 (6 files) caught one wrong assertion of mine (the prescription itself names api.documentation.description); fixed in a98ace43 to assert the located-issue line. Reverse verification, one-time, from committed 69b4d203, each mutation through scripts/ablation-replace.mjs (anchor x1 to x0 on disk, blob changed, restore proven blob == HEAD and empty git diff HEAD). Control 11/11 passed. A, the overlay line removed (blob 3f01f03a to 5c7dde2c): 9 failed / 2 passed, the survivors being exactly the nothing-authored controls. B, the helper writes into the cached info via Object.assign (3f01f03a to 662c8f32): 3 failed / 8 passed. C, the tombstone neutralised as version: z.string().optional() ?? retiredKey( (e69191ec to 1acff1f8): spec retirement test 5 failed / 18 passed. D, cross-package type proof: a planted probe in packages/rest/src made rest tsc exit 1 with src/zz-typeproof-20294.ts(3,76): error TS2322 and 0 other errors, so rest reads the rebuilt spec .d.ts; the probe was removed and its absence confirmed. Post: both blobs == HEAD, git status empty. All subjects import source, so no dist preflight applies.", "gates": [ { "cmd": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack", "exit": 0, "verdict_line": "115 derived commands; change set 16 paths vs merge base 2b24b8b82; 879 changed lines under the 5000 threshold", "tree": "69b4d203" }, { "cmd": "node scripts/pm/dispatch-gates.mjs --ran ran2.list --repo objectstack-ai/objectstack", "exit": 1, "verdict_line": "Run reconciliation — 115 derived, 113 run, 0 NOT-MEASURED, 2 UNRUN. ✗ dispatch-gates --ran: 2 of 115 derived famil(ies) UNRUN.", "tree": "69b4d203", "note": "the 2 UNRUN are the declared skips below" }, { "cmd": "113 derived gate commands (full list and per-command exit codes in the run record)", "exit": 0, "verdict_line": "112 exit 0, 1 exit 1", "tree": "69b4d203" }, { "cmd": "pnpm check:platform-checklist", "exit": 1, "verdict_line": "check-platform-checklist: 1 problem(s) — areas/identity-auth.json: ABSENT SYMBOL — packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor", "tree": "69b4d203", "note": "INHERITED from main: 0 diff lines vs origin/main under docs/qa, packages/plugins/plugin-auth and scripts; stale since 7d630889; the gate is outside per-PR CI" }, { "cmd": "pnpm --filter @objectstack/spec run check:skill-examples", "exit": 0, "verdict_line": "259 prose examples type-check across 3 surface(s)", "tree": "69b4d203", "note": "first run exit 3 PREREQUISITE NOT MET (no client-react .d.ts); rerun after building @objectstack/client + @objectstack/client-react under the lock" }, { "cmd": "pnpm check:dual-build-cjs-loads", "exit": null, "verdict_line": "NOT MEASURED", "tree": "69b4d203", "note": "reason: reads every package dist/; a whole-tree build is outside this card's local scope (CI Build Core)" }, { "cmd": "pnpm check:type-check-debt", "exit": null, "verdict_line": "NOT MEASURED", "tree": "69b4d203", "note": "reason: --re-measure rebuilds every package; the diff touches no DEBT/EXEMPT package; check:type-check-coverage ran exit 0" }, { "cmd": "pnpm --filter @objectstack/spec check:generated", "exit": 0, "verdict_line": "✓ All 15 generated artifacts are up to date", "tree": "69b4d203" }, { "cmd": "pnpm --filter @objectstack/spec check:liveness", "exit": 0, "verdict_line": "✓ packages/spec/liveness/state-counts.md is current", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 172s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 195s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 583s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/rest exec vitest run --project repo --maxWorkers=2\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 1s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/rest run typecheck\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 25s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/spec run typecheck\"", "exit": 0, "verdict_line": "VERDICT command-exit 0 · held the lock 63s", "tree": "69b4d203" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"bash ablate.sh\" (legs A-D)", "exit": 0, "verdict_line": "VERDICT command-exit 0; ABLATION-DONE; every leg red as expected, all restores blob == HEAD", "tree": "69b4d203" }, { "cmd": "PR #20512 CI at read time (one read, no polling)", "exit": null, "verdict_line": "32 check runs: 8 success, 2 skipped, 21 in_progress, 1 failure = \"The card this PR closes must claim this branch\" (PR #20512 closes #20359 with no Claim on the card naming this branch)", "tree": "69b4d203", "note": "advisory, not one of the seven required contexts; see open_questions" } ], "line_budget": "n/a — no skills/** or line-ratcheted ledger touched; diff 16 files, +777 / -102 (879 changed lines) vs the 5000 human-merge threshold: under", "files_changed": [ ".changeset/20294-openapi-info-publisher-overlay.md", "content/docs/references/api/rest-server.mdx", "packages/rest/src/rest-api-config-dead-keys-refused.test.ts", "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-openapi-info-overlay.test.ts", "packages/rest/src/rest-server.ts", "packages/spec/liveness/README.md", "packages/spec/liveness/rest_api.json", "packages/spec/liveness/state-counts.md", "packages/spec/src/api/rest-api-config-dead-keys-retirement.test.ts", "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.version.ts", "packages/spec/src/migrations/entries/semantic/18.rest-api-documentation-version-retired.ts", "packages/spec/src/migrations/registry.ts" ], "deviations": [ "PR body line 2 is Fixes #20359, as the dispatch orders. The advisory check \"The card this PR closes must claim this branch\" (scripts/check-closing-target-claim.mjs) is therefore red on #20512: #20359 has no Claim naming claude/issue-20294-openapi-info-overlay, and a dispatched dev may not post a second claim. The guard re-reads comments live, so a re-run clears it once the thread carries such a Claim. It is not among the seven required contexts.", "packages/spec/src/api/rest-server.test.ts is edited: two fixtures dropped documentation.version and one comment was updated. That file is not in the named landing sites; it falls under the dispatch's \"a pin that authors documentation.version today moves to the tombstone\". The tombstone would otherwise fail tsc and parse there.", "The rest-server.ts fence was obeyed. Two comments outside the registerOpenApiEndpoints region are now stale and were left unedited: NormalizedRestServerConfig.api.documentation near :1116 (\"inner defaults applied (.title)\") and the parseDeclaredApiConfig #14366 paragraph near :4112 (\"zero read sites\"). Neither is emitted into a published .d.ts. One-line fixes are named in the PR Acceptance notes.", "A container restart around 19:43Z killed the first full rest run, whose result was lost. Everything reported was re-run at 69b4d203 after merging origin/main 2b24b8b8 with os-regen-merge.sh. The merge left no conflict and no generated path to regenerate (check:generated all current), then pnpm install --frozen-lockfile and a closure rebuild.", "The verify lock queue-timed-out (exit 99) three times behind other seats' long holders (issue-20233 verifyC.sh about 10 min, issue-20289 verify2.sh about 15 min). Each retry used the same OS_VERIFY_LOCK_SLOT=dev-20294, and no heavy run went unlocked. Long waits were spanned by one background lock call plus a foreground tail --pid, never by polling.", "Commit trailers are the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md, not the harness reminder's model-bearing Co-Authored-By. The merge commit is os-regen-merge.sh's own. The PR body footer is the session-URL form AGENTS.md specifies, not the reminder's wording.", "check:skill-examples needed @objectstack/client and @objectstack/client-react built (9s under the lock) to leave PREREQUISITE NOT MET." ], "cleanup": "git status empty at 69b4d203 (== the remote head). /home/user/objectstack-issue-20294/node_modules removed before this report was posted, and the worktree removed with a plain git worktree remove (no --force) right after. No dev server or monitor was started; every background run launched here had exited before the report.", "mcp_calls": "0", "api_writes": "3 — each one repository_dispatch through the fleet-write relay, executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft #20512, relay run 36481580438); (2) label-write --assign os-warren → POST /repos/objectstack-ai/objectstack/issues/20512/assignees (relay run 36481673037; read back: assignee os-warren, labels documentation/size/l/tests/tooling untouched); (3) this os-dev-report → POST /repos/objectstack-ai/objectstack/issues/20294/comments via post-stamped.mjs. git push is not a REST write. The REST calls to /issues/20294(+comments), /issues/20359(+comments), /issues/comments/5405070350, /pulls?head=, /pulls/20512 and its check-runs / annotations were GETs.", "open_questions": [ { "question": "Closing-target claim guard vs line 2 of the PR body: PR #20512 says Fixes #20359 (ordered by the dispatch, and the ruling says the landing PR closes #20359). The advisory guard is red because #20359 has no Claim naming claude/issue-20294-openapi-info-overlay. How should it clear?", "options": [ "A — The PM posts a Claim on #20359 naming claude/issue-20294-openapi-info-overlay (it is the ruling carrier, so a claim-for-closure only), then re-runs the guard job. No PR edit is needed.", "B — The PM edits the PR body so line 2 reads Part of #20359, and closes #20359 by hand after the merge. The body is edited once, by the seat.", "C — Leave it red. It is advisory, outside the seven required contexts, and the queue ignores it." ], "recommendation": "A, because it keeps the ruled closure mechanical. The guard exists to prove that the closer owns the card, and a single claim line records that ownership without a body rewrite, which by protocol only the seat may make." } ], "out_of_scope_findings": [ "class: a · reach: named producer — commit 7d630889 (#20429) rewrote plugin-auth auth-plugin.ts so twoFactor is now only an inline plugins: { twoFactor: true } key. On origin/main 2b24b8b8, pnpm check:platform-checklist exits 1: \"areas/identity-auth.json: ABSENT SYMBOL — packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor\". This diff has 0 lines under docs/qa, packages/plugins/plugin-auth and scripts. The gate is outside per-PR CI, so the red is silent until someone runs it. Dedupe words: identity-auth twoFactor anchor, ABSENT SYMBOL auth-plugin.ts, check:platform-checklist red", "carrier: 承接者:无 · noted, not filed — /docs viewer HTML title literal ObjectStack API Docs (rest-server.ts registerOpenApiEndpoints, the /docs handler). Polish, not an info field (in the PR Acceptance notes). Dedupe words: ObjectStack API Docs, /docs viewer title, Scalar tab title", "carrier: 承接者:无 · noted, not filed — OpenApiGenerationConfigSchema.title third default ObjectStack API (packages/spec/src/api/plugin-rest-api.zod.ts). Dormant: 0 non-test readers against a lit control. Dedupe words: OpenApiGenerationConfigSchema title, plugin-rest-api openApi title default", "carrier: whoever next touches the rest-server.ts normalizeConfig / NormalizedRestServerConfig regions · noted, not filed — two comments there are now stale (see deviations). One-line fixes are in the PR Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20294, "status": "done", "round": "merge-only (sync with main + regenerate), requested after at-tier record 5878749194 PASSed at 69b4d203", "branch": "claude/issue-20294-openapi-info-overlay", "pr": "https://github.com/objectstack-ai/objectstack/pull/20512", "session": "session_01ARcDurZ5j34RdqsGgc4jgH — the PM's session (mode:subagent); identity = the branch named by claim 5876330236", "premise_still_valid": true, "new_head": "8e26fbbe887a339ca71d602db5dc46171281a2e2", "previous_head": "69b4d20391b1b2170d3b1c482859b81d0887647c", "merge_commit": "0945eb2169846a457ca35ef7b17e7df79ea95d2c (parents 69b4d203 + origin/main 0bbe4005)", "regeneration_commit": "8e26fbbe88 chore(spec): regenerate liveness state-counts on the merged tree", "regenerated_paths": [ "packages/spec/liveness/state-counts.md (pnpm --filter @objectstack/spec gen:liveness-counts)" ], "resolved_by_hand": "nothing. git merge auto-committed 0945eb21 with no conflict on any path. os-regen-merge.sh step 2 took main's side of the one os-regen path both sides changed, state-counts.md, and its step-3 commit was refused by pre-commit as designed (\"1 generated artifact merged WITHOUT a text merge\"). I cleared that refusal by regenerating the file with gen:liveness-counts, then git add -A and commit. No hunk was hand-edited, and there was no rebase and no force-push.", "summary": "Worktree re-created at the remote head 69b4d203 (local == remote). origin/main 0bbe4005 (#20511 landed) was merged with scripts/pm/os-regen-merge.sh. The one generated path both sides touched was regenerated on the merged tree, then pushed with a plain git push. Nothing outside the regenerated path moved. git diff origin/main..HEAD is the same 16 files, +777 / -102, as the pre-merge delta 2b24b8b8..69b4d203. Per file the changed-line set is identical for 15 of 16; state-counts.md differs only in its generated totals row, 941/147 to 949/139 instead of 940/148 to 948/140, because main moved qa from 8/1 to 9/0. state-counts.md carries both rows: `qa` 9 | 0 | 0 | 0 | 0 | 9 (#20511) and `rest_api` 20 | 0 | 0 | 4 | 0 | 24 (this PR). The hand-written README rest_api row survived (the \"Live 20 =\" and \"documentation.version tombstone (RETIRED 2026-09-28\" sentences each grep 1). registry.ts carries both of this PR's entries (2 hits) and passes its check. PR body not edited: no line in it is made false by the merge; its measurements are stated at 69b4d203.", "gates": [ { "cmd": "pnpm --filter @objectstack/spec check:generated", "exit": 0, "verdict_line": "✓ All 15 generated artifacts are up to date — measured against packages/spec/dist AS FOUND ON DISK", "tree": "8e26fbbe", "note": "dist rebuilt first: pnpm install --frozen-lockfile (the merge moved pnpm-lock.yaml), then pnpm --workspace-concurrency=2 --filter '@objectstack/rest^...' build under the lock, VERDICT command-exit 0 · held the lock 127s" }, { "cmd": "pnpm --filter @objectstack/spec check:liveness", "exit": 0, "verdict_line": "✓ packages/spec/liveness/state-counts.md is current — the same 40 row(s), no count column left in the README.", "tree": "8e26fbbe" }, { "cmd": "pnpm --filter @objectstack/spec check:migration-registry", "exit": 0, "verdict_line": "✓ src/migrations/registry.ts is current (313 semantic, 232 retired-key, 206 retired-def)", "tree": "8e26fbbe" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/spec exec vitest run --project repo --maxWorkers=2\"", "exit": 0, "verdict_line": "Test Files 39 passed (39) · Tests 701 passed (701) · VERDICT command-exit 0 · held the lock 486s", "tree": "8e26fbbe" }, { "cmd": "bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2\"", "exit": 0, "verdict_line": "Test Files 220 passed (220) · Tests 4189 passed | 40 skipped (4229) · VERDICT command-exit 0 · held the lock 188s", "tree": "8e26fbbe", "note": "the first attempt was queue-timeout exit 99 behind another seat's holder; retried on the same OS_VERIFY_LOCK_SLOT" } ], "files_changed": [ "packages/spec/liveness/state-counts.md (regeneration commit only; the merge commit brings main's own files)" ], "deviations": [], "mcp_calls": "0", "api_writes": "1 — this os-dev-report: POST /repos/objectstack-ai/objectstack/issues/20294/comments via post-stamped.mjs through the fleet-write relay. The only other write is git push 69b4d203..8e26fbbe (not REST). No PR body edit.", "cleanup": "worktree /home/user/objectstack-issue-20294: node_modules removed before this post, then removed with a plain git worktree remove.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Review: ACCEPT · PR #20512 at head
8e26fbbe887a339ca71d602db5dc46171281a2e2· 2026-09-28T22:41Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), reviewer of record under claim5876330236, with the closure claim5878320739on #20359. This seat took the readings below on GitHub or on the tree.main. Body line 1Fixes #20294, line 2Fixes #20359(the ruling orders this PR to close its carrier), andClause-②: yes (narrowing)stands alone. The closing-target claim guard is green, with the closure claim on #20359 naming this branch.5862477514) on both halves. ENFORCE: eightapi.documentationidentity members overlay the servedinfoon both doors,contact/licensereplace whole,titlebecomes.optional(), and nothing authored ⇒ the artifact'sinfo, byte-identical. RETIRE:documentation.versionis aretiredKey()tombstone with theRETIRED_KEYS_BY_MAJOR[18]key and a D3 entry, and the servedinfo.versionstaysSPEC_VERSION. Ledgerrest_api12/12 → 20/4. Changesets: spec and restminorwith the BREAKING banner.5878749194at69b4d203. It verifies every ruled parameter and the four pins per door, and the cache discipline: the overlay returns the bundled object when nothing is authored, otherwise a new object, and never writes into the cached artifact; ablation leg B's 3 red were read off the assertions. The tombstone message is true of the served value, sinceinfo.versionispkg.versionof@objectstack/spec, a different number fromPROTOCOL_VERSION. The retirement route matches thedocumentation.enabledprecedent.69b4d203→8e26fbbe: the merge oforigin/main0bbe4005(#20511) throughos-regen-merge.sh, plusgen:liveness-countson the merged tree. Nothing was hand-resolved.Regen-provenance:5879799287carries the record forward. This seat checked the committed trees: 15 of 16 files have an identical changed-line set, andstate-counts.mddiffers only in its generated totals row, by #20511'sqa8/1 → 9/0. The dev re-ran spectest:repo(701 passed) and the rest suite (4189 passed) at8e26fbbe.check-expected-skips.mjs --pr 20512, exit 0).mergeable_state:clean.git merge-treeagainstorigin/main0bbe4005is clean.check-governed-merges.mjs --pr 20512: 0 of 16 paths), 879 changed lines.Out-of-scope findings:
rest-server.tsoutside the fenced region (NormalizedRestServerConfig.api.documentationnear :1116, theparseDeclaredApiConfigparagraph near :4112). The record corrects the dev: the :4112 JSDoc most likely ships on a private stub in@objectstack/rest's.d.ts. Both are prose with nil reach, and not an a/b/c finding. Carrier: this seat's next dispatch that touchesrest-server.ts, as two one-line comment fixes named in its order./docsviewer's literal HTML title, andOpenApiGenerationConfigSchema.title's dormant third default → Acceptance notes, not a/b/c, no carrier owed.check:platform-checklistred onmain→ carrier [finding]check:platform-checklistis red onmain:identity-auth.jsonanchorsauth-plugin.ts#twoFactor, which #20429 turned into an inline nested key #20464.Landing next: ready + auto-merge through the relay. At MERGED, the
Fixeslines close #20294 and #20359.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHobjectstack-fleet commentedon Sep 28, 2026
Landed: PR #20512 →
main80153f5a4a9c641e78b21cd84a35069a84192a96· 2026-09-28T23:06Zdomain:specseat 4 (session_01ARcDurZ5j34RdqsGgc4jgH), landing record for claim5876330236. ACCEPT5880040657at8e26fbbe, on the at-tier PASS5878749194carried over one pure-regeneration hop byRegen-provenance:5879799287. Merged through the merge queue.Verified on
origin/main:80153f5ahas one parent,9e1689f8, and is an ancestor oforigin/main.overlayDocumentationInfoinpackages/rest/src/rest-server.ts0 → 3; the retired-key entry18.api__RestApiConfig__documentation.version.tsis present.state-counts.mdreadsrest_api20 live / 4 dead / 24, beside feat(spec,core,cli)!: a scenario's requires is checked before it runs — unmet params or services skip it with a reason; requires.plugins retires into requires.services #20511'sqarow.Close-out: the PR's
Fixeslines closed this card and #20359completed, as ruling B ordered.pm:dispatchedcomes off both in this act. Ruling5862477514is executed: the eight identity members are honoured on both OpenAPI doors, anddocumentation.versionis retired, withinfo.versionstayingSPEC_VERSION. Carried from the ACCEPT: two stalerest-server.tscomments ride this seat's next dispatch that touches that file.domain:specseat 4 · #18917 ·session_01ARcDurZ5j34RdqsGgc4jgHfeat(spec,rest)!: the served OpenAPI info carries the publisher's api…
test(spec): the second api/ file group's test titles state each cited…