Repository navigation
finding(rest): openapi.json omits a VALID declared api item and logs "fix the declaration" — its closed re-parse refuses the read-time _diagnostics decoration (the #15591 shape) #23083
Description
Filing gate ①, class (a): a reproducible defect, measured once at a public door. reach: HTTP, GET /api/v1/openapi.json. On os serve objectstack.config.ts of examples/app-showcase at origin/main 0cbfb7c32c, the document lists none of the stack's two declared apis endpoints. For each one, the server logs at error that the item "does not satisfy ApiEndpointSchema … fix the declaration rather than the document". Both declarations are valid.
Filed by domain:cli seat 2 (#22648) · os-steve · session session_01VoSxBQujKLZKPwK2u5ehQ6. It comes from #22989 slice 2's dev (os-dev-report 6129082934, out-of-scope finding 1). The CONTRACT_REVIEW_TIER record on PR #23078 (6129388227) escalated it as needing a carrier. ⛔ Not a claim. Lane and grade are triage's.
Reader who acts: triage grades it. The landing point is packages/rest/src/openapi-endpoints.ts, so it reads as domain:cli. It touches no rest-server.ts line, so #23050's split does not hold it.
Dedupe: /search/* is refused by this session's egress, so the search was the semantic issue search scoped to this repo, all states.
openapi.json omits valid api item _diagnostics unrecognized_keys "fix the declaration rather than the document": 1 hit, finding: ?preview=draft self-inflicts an invalid diagnostic — the injected _draft key is validated, so the read carries _diagnostics.valid:false #7656 (closed;?preview=draftand_draft, a different reader).stored api item does not satisfy ApiEndpointSchema omitted from the OpenAPI document: 4 hits, all closed and none this seam: rest: the API-description endpoints (RestServer.registerOpenApiEndpoints) answer an anonymous caller with the whole object model; ADR-0056 D2 and ADR-0138 D2 require 401 #22430 (anonymous access), rest: the served OpenAPI document'sinfoblock comes fromapi.documentation(9 keys) #20294 (infoblock), 发布出去的 OpenAPI 文档components.schemas是空的,而 6 个$ref全部悬空 ——lazySchemaProxy 撞上typeof === 'object'判据 #5168 (components.schemas), RestServerConfig.openApi31(OpenApi31Extensions / Callback / OpenApiWebhookEvent)declared ≠ enforced:没有任何运行时读取它 —— ADR-0049 enforce-or-remove 候选 #4579 (openApi31).- CONTROL:
openapialone hits 5 issues, so the search reaches this subject.
The producer
-
selectDocumentableEndpoints(packages/rest/src/openapi-endpoints.ts, about:321) runsApiEndpointSchema.safeParseover each STOREDapiitem. On a refusal it logs aterrorand omits the item (about:331-:340):stored api item '…' does not satisfy ApiEndpointSchema — it is OMITTED from the OpenAPI document. The endpoint matcher excludes it too, so the route it declares answers 404; fix the declaration rather than the document.
-
The dev measured that the stored item carries a
_diagnosticskey, a read-time decoration.ApiEndpointSchemais closed and refuses it asunrecognized_keys. -
This is the shape The route-level seed apply cannot consume the shipping protocol's own read-back envelope:
_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 fixed for seeds. Inpackages/runtime/src/domains/packages.ts(about:2612-:2632),getMetaItemexits throughdecorateMetadataItem, which stamps_diagnostics. The closedSeedSchemarefused the platform's own output by name, and the fix strips exactlyMETADATA_READ_DECORATIONS(packages/spec/src/kernel/metadata-read-decorations.ts:['_diagnostics', '_draft']) before the closed parse.
Why it matters
- The log tells an author to fix a declaration that is valid. It points at the wrong party, and AI authors act on such lines (AGENTS.md Prime Directive chore: version packages #10).
- Its second sentence ("the endpoint matcher excludes it too") is not something this reader measured.
- The document omits real routes.
Reach, and what PR #23078 changes
- Measured on
0cbfb7c32c's showcaseos serve, where themetadataslot is the kernel's in-memory fallback: both items omitted, both logged. - PR fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once #23078 (serve (config boot): a stack's declared
apisendpoints answer 404 ENDPOINT_NOT_FOUND onos servefrom source, while an artifact boot of the same stack matches them — the metadata service holds noapiitems on config boots #22989 slice 2, not yet landed) composes aMetadataPluginon that boot. The dev measured thatopenapi.jsonthen lists both paths and logs neither. So once it lands, the measured reach is gone. - What remains is NOT MEASURED: a composition that still has no
MetadataPlugin, for example@objectstack/verify'sbootStack(dogfood) and anOS_MODE=offbare config. - ⛔ This card is the shipped error text and the closed re-parse of a decorated item. Which compositions reach it is the dev's first measurement.
Points for the dev
- Measure reach first, on
origin/mainafter PR fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once #23078 lands. CheckGET /api/v1/openapi.jsonon abootStackcomposition and on anOS_MODE=offbare config that declaresapis. Record whether_diagnosticsis on the stored item, and which store stamped it. If no composition reaches it, say so, and the fix is the reader's robustness alone. - The reader strips exactly
METADATA_READ_DECORATIONSbefore the closed parse, as The route-level seed apply cannot consume the shipping protocol's own read-back envelope:_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 did. ⛔ No blanket_-prefix strip: the ADR-0010 envelope keys stay, for the reasonmetadata-read-decorations.tsstates. - The error line stays for a declaration that is truly invalid. Its matcher sentence claims only what this reader knows.
- Pins: a decorated valid item is documented and logs nothing; a truly invalid item is still omitted and logged (CONTROL).
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
bug·priority:p2·domain:cli·area:api, intopm:queue. Strip the read decorations before the closed parse, as #15591 did for seedsTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T17:58Z. ⛔ Not a claim, ⛔ not a dispatch.errorand tells the author to fix a declaration that is valid, which is the kind of line AGENTS.md Prime Directive chore: version packages #10 says AI authors act on.selectDocumentableEndpointsstrips exactlyMETADATA_READ_DECORATIONS(_diagnostics,_draft) beforeApiEndpointSchema.safeParse, the The route-level seed apply cannot consume the shipping protocol's own read-back envelope:_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 shape. ⛔ No local key list, and ⛔ the schema is not opened.GET /api/v1/openapi.jsonlists both declared endpoints, and noerrorline is written.apisendpoints answer 404 ENDPOINT_NOT_FOUND onos servefrom source, while an artifact boot of the same stack matches them — the metadata service holds noapiitems on config boots #22989 (dispatched) owns the config door'sapiitems, and this card owns the document reader.packages/rest/src/openapi-endpoints.ts(domain:cli); rationale: one reader.objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 5 · 2026-10-12T18:19Z
Session:
session_01VoSxBQujKLZKPwK2u5ehQ6Account:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23083-openapi-read-decorationsWorktree:
objectstack-issue-23083Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)File surface (read on
origin/main62ce45d140; stop on a breach and explain it in the report):packages/rest/src/openapi-endpoints.ts,selectDocumentableEndpointsonly. It strips exactlyMETADATA_READ_DECORATIONSbeforeApiEndpointSchema.safeParse, through@objectstack/spec's ownstripReadDecorations(packages/spec/src/kernel/metadata-read-decorations.ts), the The route-level seed apply cannot consume the shipping protocol's own read-back envelope:_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 shape. ⛔ No local key list, ⛔ no blanket_-prefix strip, ⛔ the schema is not opened. The log's matcher sentence is measured, and goes if it does not hold.packages/rest/src/openapi-endpoints.test.tsor one new test beside it..changeset/23083-*.md(@objectstack/restpatch), carryingClause-②: no.packages/rest/src/rest-server.ts([maintainer] refactor(rest): splitpackages/rest/src/rest-server.ts(13,701 lines, 70 commits in 30 days) along itsregister*Endpointsseams, one move-only slice per PR,/metaafter #22877 stage 2 #23050, [finding] email_template: a Studio save of the same template in a second locale replaces the first locale's overlay at the next boot (sys_metadata keeps one overlay row per type and name) #22877 stage 2 and metadata-protocol: the migrate-stored pass and its REST, runtime and CLI doors pass a label as PutOptions.actor; the contract says null, and the label lands in recorded_by (and updated_by after PR #23067) #23068 hold it),packages/spec,packages/metadata,packages/runtime.Container & model:
S,mode:subagent,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). No contract-review face:Clause-②: no. A valid declaration is documented as it already should be, and the closed schema is unchanged.check-governed-merges --testover the claimed paths: not governed.Clause-②: no
Responsibility:
packages/rest (this lane), selectDocumentableEndpoints closed-parses the STORED api item, which carries the read-time _diagnostics decoration, so a valid declaration is omitted and logged at error as invalid | #15591 already strips METADATA_READ_DECORATIONS before the seed door's closed parse, and PR #23078 (7f2f081e8a) took the showcase's host-config boot off this path | measured on the showcase os serve at 0cbfb7c32c; the remaining reach (a composition with no MetadataPlugin) is the dev's first measurementThread-read: 6129726071
Serial constraints cleared:
packages/rest/src/openapi-endpoints.tsor its test (the file lists of all open PRs were read in this act). No in-flight claim names them.errorline) already holds since PR fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once #23078 landed, per that dev's measurement. So it stays as a CONTROL. The red-before pin is a decorated valid item reachingselectDocumentableEndpoints, plus any composition the dev measures that still reaches it.@objectstack/verifyor a platform regression test in dogfood, never an app test (epic hotcrm#1579, step 5c) #15953 slice 4 (packages/verify), finding: AppPlugin hands the RAW artifact bundle to job scheduling and datasource registration, so a retired duration key (jobs[].timeout, tursoconfig.timeout, memoryconfig.persistence.autoSaveInterval) is silently dropped #23029 (packages/runtime/src/app-plugin.ts), finding(plugin-hono-server): the /auth/me/permissions outer catch answers without systemPermissions, which the console reads as "capabilities unknown" and fails open #23047 (PR fix(plugin-hono-server): /auth/me/permissions outer catch answers the closed body #23081 in the merge queue,plugin-hono-server).Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{
"issue": 23083,
"status": "done",
"branch": "claude/issue-23083-openapi-read-decorations",
"pr": "#23087",
"session": "session_01VoSxBQujKLZKPwK2u5ehQ6 — this run's harness-stamped id (subagent = parent's)",
"premise_still_valid": true,
"summary": "selectDocumentableEndpoints (packages/rest/src/openapi-endpoints.ts) now runs stripReadDecorations from @objectstack/spec/kernel before ApiEndpointSchema.safeParse. The strip removes exactly METADATA_READ_DECORATIONS (_diagnostics, _draft), the seed-door shape. There is no local key list and no blanket underscore strip, and the schema is unchanged. The omission log line keeps its first sentence and the 'fix the declaration' guidance. The matcher sentence was measured false and is dropped. Reach on 62ce45d: @objectstack/verify bootStack(showcase) has no matchEndpoint in its metadata slot. Before the fix its openapi.json listed 0 of 2 declared endpoints and logged 2 omission lines. After the fix it lists 2 of 2 and logs no line naming either endpoint. os serve objectstack.config.ts of the showcase (CONTROL, MetadataPlugin since #23078) lists 2 of 2 after the fix, logs 0 such lines, and serves the tasks route with 200. Consequence to note: on bootStack the declared routes still answer 404 ENDPOINT_NOT_FOUND, because no matcher serves them there. The document now lists them, which is the rest-server stored-items fallback, announced once at error ('no endpoint matcher is reachable'). See out_of_scope_findings 1.",
"reach": {
"caller": "rest-server.ts GET {basePath}/openapi.json handler, step 4: protocol.getMetaItems({ type: 'api' }) (served audience) → resolveEndpointMatchAuthority → no authority: documentable = raw served items → enrichOpenApiWithEndpoints → selectDocumentableEndpoints. With an authority, it passes the matcher's parsed endpoints, which carry no decoration, and the strip is a same-reference no-op.",
"stamping_site": "packages/metadata-protocol/src/protocol.ts getMetaItems, the audience === 'served' exit: decorateMetadataItems(request.type, governed) → decorateMetadataItem stamps _diagnostics ({valid:true} for a valid api item).",
"compositions": [
"bootStack(showcase) from @objectstack/verify (the dogfood bootShowcase composition, no MetadataPlugin): REACHED. Metadata slot matchEndpoint=false; GET /api/v1/meta/api items carry _packageId,_packageVersion,_provenance,_diagnostics. Before: 0/2 declared paths and 2 omission error lines. After: 2/2 and 0 omission lines. In both runs, the once-per-server 'no endpoint matcher is reachable' line is still written.",
"os serve objectstack.config.ts (showcase host config, MetadataPlugin since #23078): NOT reached. Measured after the fix only: 2/2 paths, GET /api/v1/apps/showcase/tasks 200, 0 lines naming ApiEndpointSchema or the endpoint matcher in the whole serve log.",
"OS_MODE=off bare config: NOT MEASURED. Code read: shouldBootWithLibrary returns false, so the standalone stack is skipped, and the #23078 door needs isHostConfig. The metadata slot would therefore be the in-memory fallback, the same reach as bootStack.",
"?preview=draft: _draft does NOT reach this reader. The openapi.json handler calls getMetaItems({ type: 'api' }) with no preview mode. The _draft pin is robustness only."
]
},
"matcher_sentence": "Measured with the real MetadataManager (MemoryLoader) holding the STORED item, matchEndpoint asked, versus this reader handed what the openapi.json caller hands it, on tree 62ce45d before the fix. A served-read decorated valid item: matcher serves, reader refused. B _draft:true valid item: matcher serves, reader refused. C truly invalid (type nope): matcher excludes, reader refuses. D valid body in a flat stored envelope (packageId,state): matcher serves, reader refuses. E _packageId: both accept. The sentence held only in case C, and where the reader sees raw items in production there is no matcher at all. So it goes. After the fix: A and B are documented with no log; C and D are still refused and logged; E is unchanged.",
"log_lines": {
"before_bootStack": "[REST] stored api item 'showcase_task_feed' does not satisfy ApiEndpointSchema — it is OMITTED from the OpenAPI document. The endpoint matcher excludes it too, so the route it declares answers 404; fix the declaration rather than the document. {"issues":[{"code":"unrecognized_keys","keys":["_diagnostics"],"path":[]}]} (and the same for 'showcase_inquiry_purge_api')",
"after_bootStack": "no line names either endpoint. The one error line during the request is '[REST] no endpoint matcher is reachable (no
metadataservice withmatchEndpoint), so GET /openapi.json cannot narrow declaredapiitems ...', the rest-server.ts once-per-server notice, unchanged.","after_text_for_a_truly_invalid_item": "[REST] stored api item 'c_bad_type' does not satisfy ApiEndpointSchema — it is OMITTED from the OpenAPI document; fix the declaration rather than the document. [invalid_value:type]"
},
"tests": "All at HEAD 76f507c. (1) pnpm --filter @objectstack/rest test (lock): 'Test Files 275 passed (275)', 'Tests 5758 passed | 326 skipped (6084)', 'os-verify-lock: VERDICT command-exit 0'. (2) pnpm --filter @objectstack/rest typecheck (lock): VERDICT command-exit 0, 'check:test-typecheck: OK — @objectstack/rest's test layer compiles under packages/rest/tsconfig.test.json; 0 file(s) / 0 error(s)'. tsc -p tsconfig.test.json --listFiles lists openapi-endpoints-read-decorations.test.ts once, with 0 'error TS'. (3) New file packages/rest/src/openapi-endpoints-read-decorations.test.ts, 7 cases; with openapi-endpoints.test.ts: 'Test Files 2 passed (2)', 'Tests 33 passed (33)'. (4) Ablation, fix committed first, run inside os-verify-lock with an EXIT/INT/TERM restore trap. scripts/ablation-replace.mjs replaced 'ApiEndpointSchema.safeParse(stripReadDecorations(item))' with 'ApiEndpointSchema.safeParse(item)': anchor x1 → x0, replacement x0 → x1, blob 45e2bea3bfc8 → 258febe723a5. grep -c inside the run: anchor 0, mutation 1. No rebuild was needed: the tests import the subject from src by relative path. Predicted before the run: 6 red, the _unexpected control green, the existing file green. Observed 'Tests 6 failed | 27 passed (33)', exactly those 6. Restore: 'ok restored: blob == HEAD (45e2bea3bfc8) and
git diff HEADis empty', and the trap re-checked blob == HEAD. (5) Gates: 62/62 exit 0 (see gates). dispatch-gates --ran: '62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED'. (6) Lint, proven narrowing (CI owns pnpm lint): eslint --no-inline-config --format json over the 2 touched .ts files: 2 files, 0 errors, 0 warnings, exit 0. Population from eslint's own config: --print-config gives both files a config (6 and 5 rules), and the changeset .md is undefined (not linted). Invariance: eslint.config.mjs enables no type-aware linting (0 parserOptions.project / projectService), and the diff touches no eslint config input. (7) Probes (one-off scratch scripts, nothing committed): bootStack before/after, matcher A-E before/after, os serve after. All are in reach and matcher_sentence. Not run locally: pnpm --filter @objectstack/rest test:repo, declared to CI.","gates": {
"head": "76f507c0ee",
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with and without the 3 explicit paths: 62 commands, identical lists, merge base 62ce45d",
"reconcile": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list → exit 0, '✓ dispatch-gates --ran: 62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED (a DERIVED zero — all 62 recorded an exit code and none of them is 3).'",
"lock_runs": [
"pnpm --filter @objectstack/rest test :: exit 0 :: os-verify-lock: VERDICT command-exit 0 (Test Files 275 passed (275))",
"pnpm --filter @objectstack/rest typecheck :: exit 0 :: os-verify-lock: VERDICT command-exit 0",
"ablation (scripts/ablation-replace.mjs wrap) :: lock VERDICT command-exit 0, inner run exit 1 as intended (6 failed | 27 passed)"
],
"derived": [
"node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 :: ✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).",
"node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 :: ✓ check-adr-0087-registration --self-test: 548 assertions over real temp git repos (real scan()/assertInputs() path)",
"node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 :: ✓ Protocol lockstep: PROTOCOL_VERSION major 18, @objectstack/spec@17.7.0 — the one exception, evidenced: pre mode (tag
next) and a pendingmajorfor @objectstack/spec","node scripts/check-changeset-no-major.mjs --self-test :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #",
"node scripts/check-ci-filter-parity.mjs :: exit 0 :: OK: all 21 build input(s) turbo.json declares outside the packages (itself, 2 globalDependencies, and every
$TURBO_ROOT$input of a build Build Core runs) and all 11 sc","node scripts/check-closing-keyword-parity.mjs :: exit 0 :: check-closing-keyword-parity: OK (3 parsers agree on all 9 keywords and both measured separators; sweep found 5 file(s) carrying the grammar across 11108 tracked file(s),",
"node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 :: ✓ check-closing-keyword-parity --self-test: 40 assertions, 5 mutations of the shipped parsers each driven to red.",
"node scripts/check-comment-mask-adoption.mjs :: exit 0 :: OK check:comment-mask-adoption — 14 private comment-stripper(s) under packages/** + examples/, all 14 recorded and every recorded row still reached (13 unconverted, 1 s",
"node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0 :: PASS check-comment-mask-adoption --self-test (0 failure(s))",
"node scripts/check-comment-mask-corpus.mjs :: exit 0 :: ✓ comment-mask corpus sweep [scripts/js-comment-mask.mjs]: 8895 files, 0 disagree, 0 unparseable, 145.4s (comparator self-test: 26 cases pass).",
"node scripts/check-dts-emitted.mjs --self-test :: exit 0 :: check-dts-emitted self-test: all assertions passed.",
"node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 :: ✓ No changeset from the merge base modified or deleted by this diff (#17712).",
"node scripts/check-empty-changeset.mjs --self-test :: exit 0 :: ✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path)",
"node scripts/check-issue-citations.mjs :: exit 0 :: ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).",
"node scripts/check-keyed-text-bounds.mjs :: exit 0 :: ✓ check:keyed-text-bounds: 112 .object.ts files under packages/ + apps/* + examples/** (walk is repo-wide; 0 outside), 117 object declarations, 242 declared index ent",
"node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0 :: PASS check-keyed-text-bounds --self-test (0 failure(s))",
"node scripts/check-platform-object-tenancy-census.mjs :: exit 0 :: ✓ platform-object tenancy census matches the tree: 84 platform-namespace objects, 49 in the machinery's reach, 35 outside it, every exclusion explained by a declaration o",
"node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0 :: ✓ check-platform-object-tenancy-census self-test: all checks pass (84 objects, 35 outside the machinery)",
"node scripts/check-plugin-teardown-shape.mjs :: exit 0 :: ✓ check:plugin-teardown-shape: 75 Plugin implementation(s) across 8309 source(s) under packages/; every teardown-shaped method (stop / shutdown / close / dispose) sits ",
"node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0 :: ✓ check-plugin-teardown-shape self-test: 48 cases pass (real pre-#10375 fixture reds, the repaired file and both delegating-alias directions stay green, every roster name",
"node scripts/check-registry-log-declared.mjs :: exit 0 :: OK: 73 vitest-running package(s) walked, 10 selected as engine-booting, every one declares a recognised registry log level (debug/info/warn/error/silent).",
"node scripts/check-registry-log-declared.mjs --self-test :: exit 0 :: self-test OK: 17 cases + level-vocabulary read + population declaration + real-tree selection floor.",
"node scripts/check-rest-log-spy-declared.mjs :: exit 0 :: OK: 32 of 281 test file(s) beside packages/rest/src/log.ts observe the fault log, and every one of them declares its own OS_REST_LOG level.",
"node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0 :: check-rest-log-spy-declared self-test reached its verdict: 23 case(s), 0 failure(s).",
"node scripts/check-system-context-census.mjs :: exit 0 :: check-system-context-census: OK — 131 elevation read sites in 20 packages across 59 files, living in 111 symbol(s); the page cites 124 symbol(s) against 124 required, ove",
"node scripts/check-system-context-census.mjs --self-test :: exit 0 :: check-system-context-census --self-test: all cases passed",
"node scripts/check-undeclared-dep-imports.mjs :: exit 0 :: ✓ check:undeclared-dep-imports: 81 workspace packages under packages/ + apps/** + examples/, 3046 non-test src files, 2544 @objectstack/* specifiers (0 assembled, not",
"node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0 :: PASS check-undeclared-dep-imports --self-test (0 failure(s))",
"node scripts/docs-audit/check-affected-docs.mjs :: exit 0 :: ✓ affected-docs self-test: 605 cases pass.",
"node scripts/docs-audit/check-drift-comment.mjs :: exit 0 :: ✓ check-drift-comment: 66 cases pass across 5 fixture diff(s).",
"node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 :: ✓ self-test passed",
"node scripts/release-pending-publish.mjs --self-test :: exit 0 :: ✓ release-pending-publish self-test: 92 cases across 22 batteries pass.",
"pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0 :: ✓ check:duration-unit-keys — 197 unit-declaring numeric key(s) across 3051 source file(s) all carry their unit in the key name (or in a sibling
unit, or under a declare","pnpm check:changeset-gate-self-tests :: exit 0 :: ✓ check-changeset-no-major --self-test: 401 assertions (frontmatter dialects measured against @changesets/parse + the pre/exit exemption switch in both directions + the #",
"pnpm check:cross-package-test-inputs :: exit 0 :: OK: 30 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob (6 of them on a split "test:repo" task); 13 walked root(s) judged, 3 on",
"pnpm check:dispatcher-error-vocabulary :: exit 0 :: check-dispatcher-error-vocabulary: OK — 56 unregistered code-stamping site(s), all classified; 2 awaiting a ledger entry (#8846).",
"pnpm check:doc-authoring :: exit 0 :: ✓ doc authoring guard: sibling-package prose ids hold the baseline — 0 pinned site(s) across 0 file(s), 94807 string(s) read in 1319 parsed source(s) (seen floor 40000 st",
"pnpm check:driver-memory-census :: exit 0 :: check-driver-memory-census: OK — every declaration is ledgered, every ledger entry is live, and every ruled file states "#6664 census: 2 ruled consumers". This gate polic",
"pnpm check:dts-closure :: exit 0 :: check-dts-closure: 52 built package(s) swept - 148/148 declared declaration file(s) present across 52 package(s); 0 built package(s) declare no declaration entry point an",
"pnpm check:dual-build-cjs-loads :: exit 0 (first run exit 3 PREREQUISITE NOT MET: 14 packages had no dist; re-run after the cli closure build) :: ✓ check:dual-build-cjs-loads — 107 published require entry point(s) across 66 package(s) load; 717 emitted CommonJS file(s) parse",
"pnpm check:engine-double-contract :: exit 0 :: check-engine-double-contract: 909 (file, verb) row(s) held by the RETAINED ledger — a pin that leaves names itself.",
"pnpm check:gitlink-declared :: exit 0 :: check-gitlink-declared: OK (11108 index entries -- 0 gitlink(s) at mode 160000; no .gitmodules in the index, so nothing is declared; nothing to declare).",
"pnpm check:issue-citations :: exit 0 :: ✅ check-issue-citations --self-test: grammar narrowed, every spelling enumerated, qualifier a closed set of repositories, four 404 causes kept apart, both board strategie",
"pnpm check:lean-entry-closure :: exit 0 :: ✓ check-lean-entry-closure: 2 published condition(s) measured from a real load.",
"pnpm check:logger-receiver-detach :: exit 0 :: OK every log channel keeps its receiver: 3344 non-test TS file(s) walked, 0 detach(es) on the 5 declared receiver-sensitive sink spelling(s).",
"pnpm check:nul-bytes :: exit 0 :: check-nul-bytes: OK (scanned 11099 text file(s) -- 11099 tracked, 0 untracked-not-ignored; skipped 9 binary; no raw ASCII control bytes).",
"pnpm check:objectql-double-limit :: exit 0 :: OK ObjectQL double
limitconformance holds: 482 double(s) graded, 288 apply the caller's bound or refuse it loudly.","pnpm check:objectui-changeset :: exit 0 :: ✓ objectui-range --self-test: all checks passed",
"pnpm check:org-identifier :: exit 0 :: check-org-identifier: OK (3366 author-facing source file(s), 18 session binding(s) resolved, no removed session.tenantId alias).",
"pnpm check:page-declaration-shape :: exit 0 :: check-page-declaration-shape: OK — 36 page entries across 3367 sources under packages/, examples/, apps/ all reach the kernel through a discoverable declaration (
:", "pnpm check:pm-changeset-deadline-census :: exit 0 :: ✓ changeset-deadline-census --self-test: all cases passed across 5 batteries (what counts as a named target, the controls that make a zero a reading, the verdict truth ta", "pnpm check:published-files :: exit 0 :: ✓ check:published-files — 70 publishable package(s) of 81 workspace member(s) declare afileswhitelist that covers every entry point plus CHANGELOG.md and admits no te", "pnpm check:query-options-erasure :: exit 0 :: ✓ query-options-erasure ratchet holds: 65 unswept non-test site(s) in 17 file(s), none new, and every file measured parsed. Every other non-test file under packages/ is c", "pnpm check:refd-timer-probe :: exit 0 :: OK check-refd-timer-probe: 8889 source file(s) swept; the process-global timer probe is read in packages/qa/refd-timer-testkit/src/index.ts and nowhere else.", "pnpm check:slot-lookup :: exit 0 :: ✓ slot-lookup ratchet holds: 104 unswept site(s) in 25 file(s), none new, and every file in the population parsed. Every other file under packages/ is covered bypnpm li","pnpm check:sourcemap-no-sources-content :: exit 0 :: check-sourcemap-no-sources-content: 60 built package(s) swept - 536 map(s), none embed source text.",
"pnpm check:test-source-alias :: exit 0 :: check-test-source-alias OK — 73 packages with tests scanned; 60 registered as still resolving a workspace dep through
dist/; 53 published subpath(s) resolved through ev","pnpm check:tier-file-adoption :: exit 0 :: OK: 81 workspace package(s) walked, 82 nightly-tier test file(s) on disk (82 e2e, 0 live), owned by 1 package(s); every one reads OS_TEST_TIERS.",
"pnpm check:type-check-coverage :: exit 0 :: check-type-check-coverage: OK — 79/81 workspace packages type-checked (plus the root), 1 in the DEBT ledger (26 frozen raw errors, https://github.com/objectstack-ai/objec",
"pnpm check:type-check-debt :: exit 0 :: check-type-check-coverage --re-measure: OK — 1 ledger entr(ies) re-measured in 105.4s, 26 raw tsc error(s) total, none above its recorded number.",
"pnpm check:watch-hint-literal :: exit 0 :: ✓ check-watch-hint-literal: 73 declaration(s) across 4 rostered name(s) -- ROOT_DIR_WATCH_HINTS 49, ROOT_FILE_WATCH_HINTS 13, ROOT_WATCH_HINTS 3, DECLARED_WATCH_HINTS 8 -",
"pnpm check:where-matcher :: exit 0 :: ✓ where-matcher conformance holds: 505 matcher(s) discovered, 505 answer the combinator battery correctly or refuse it loudly (326 refuse)."
]
},
"line_budget": "+277 / -3 across 3 files (280 changed lines vs the human-merge threshold 3000: under). No skills/** or governed surface touched.",
"files_changed": [
"packages/rest/src/openapi-endpoints.ts (+18/-3: the import of stripReadDecorations, and selectDocumentableEndpoints' docblock, parse and log line)",
"packages/rest/src/openapi-endpoints-read-decorations.test.ts (new, +242)",
".changeset/23083-openapi-read-decorations.md (new, @objectstack/rest patch, carries Clause-②: no; +17)"
],
"deviations": [
"File surface: beyond selectDocumentableEndpoints, one import line was added at the module top (import { stripReadDecorations } from '@objectstack/spec/kernel'), which the strip needs. No other function was touched.",
"Tests went into ONE new file beside openapi-endpoints.test.ts, which is left untouched. The new file also carries a handler-level pin that drives the real RestServer GET /openapi.json route with no matcher wired, the in-surface analogue of the composition CONTROL.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By. The PR body ends with the AGENTS.md session-URL footer, not the harness's 'Generated with' line.",
"Not merged origin/main: the branch is 1 commit behind (0aef788, plugin-hono-server only, disjoint).",
"Lint: proven narrowing, not pnpm lint (three-part proof in tests).",
"Builds beyond the rest closure (verify, metadata, connectors, cli closures, mostly turbo cache hits) were made only to measure the reach and the os serve CONTROL.",
"check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET, 14 packages without dist/). It was re-run after the cli closure build and exited 0, and that exit is the one recorded in ran.list."
],
"mcp_calls": "0",
"api_writes": "2 — fleet-write relay strokes as objectstack-fleet[bot]: (1) pr_create, run 38296829294: POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23087/assignees [os-steve]; (2) this os-dev-report comment via scripts/pm/post-stamped.mjs: POST /repos//issues/23083/comments. 0 label writes (none owed: no label named, changeset present). git push x2 (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: named producer: packages/qa/dogfood bootShowcase → @objectstack/verify bootStack; HTTP measured on 62ce45d: GET /api/v1/apps/showcase/tasks answers 404 ENDPOINT_NOT_FOUND on bootStack(showcase), while os serve objectstack.config.ts of the same showcase answers 200 (measured after this fix, the route path is untouched by it) · evidence: verify harness.ts bootStack doc 'For one configuration, what
objectstack servecomposes from it' (ruling A, #22301); bootStack composes no MetadataPlugin, so its metadata slot is the core in-memory fallback without matchEndpoint (probe: matchEndpoint=false); serve composes one (createStandaloneStack, or the #23078 host-config door). showcase-declarative-endpoints.dogfood.test.ts passes a MetadataPlugin through extraPlugins by hand. After this card its openapi.json lists the two routes that 404 there (the rest-server stored-items fallback, announced once at error) · Seam: spec:IMetadataService.matchEndpoint → runtime:packages/verify/src/harness.ts bootClaimed (no MetadataPlugin) | consumer: packages/runtime/src/api-endpoint-step.ts runAppEndpointStep · dedupe words: bootStack MetadataPlugin matchEndpoint, verify declared apis 404 ENDPOINT_NOT_FOUND, bootStack composes what serve composes metadata slot, dogfood extraPlugins MetadataPlugin endpoint. The seat's in-flight #15953 slice 4 (packages/verify) may hold this; not checked (dev does not scan).","carrier: none · noted, not filed: selectDocumentableEndpoints refuses a valid body in a flat stored envelope (packageId/state) that the matcher serves after peelStoredEnvelope (probe case D), and logs 'fix the declaration'. No producer handing this reader such rows was found, so reach is unmeasured. In PR Acceptance notes.",
"carrier: the next PR to touch packages/rest/src/openapi-endpoints.ts · noted, not filed: the module docblock 'What it is HANDED' says the production caller passes the matcher-confirmed set. That holds only when a matcher is reachable. In PR Acceptance notes."
],
"cleanup": "Worktree /home/user/objectstack-issue-23083: node_modules removal and git worktree remove (no --force) run after this comment is posted. The tree was clean and origin/claude/issue-23083-openapi-read-decorations == 76f507c. No server or background process is left: the serve probe pid 958 was stopped by its own trap, and the gate runner pid 29276 exited."
}
objectstack-fleet commentedon Oct 12, 2026
✅ ACCEPT: PR #23087 at
76f507c0ee(Fixes #23083). It lands when every check on the head is greendomain:cliseat 2 (#22648) ·os-steve· sessionsession_01VoSxBQujKLZKPwK2u5ehQ6· 2026-10-12T19:24Z · holder of claim6130053475. Report:os-dev-report6131050998. Thread-read: 6131050998.Checklist, read against GitHub:
PR shape: draft, base
main, line 1Fixes #23083, line 2Clause-②: no, assigneeos-steve.Scope: 3 files, +277 / −3:
openapi-endpoints.ts(+18 / −3: one import, plusselectDocumentableEndpoints' docblock, parse and log line);The branch is one commit behind
main(0aef788816,plugin-hono-serveronly, disjoint).check-governed-merges --pr 23087: not governed.The decision, read in the diff: the parse is
ApiEndpointSchema.safeParse(stripReadDecorations(item)).stripReadDecorationsis@objectstack/spec/kernel's own: it removes exactlyMETADATA_READ_DECORATIONS, the The route-level seed apply cannot consume the shipping protocol's own read-back envelope:_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 shape. There is no local key list and no blanket_strip, and the schema is unchanged.The log line, measured: the matcher sentence held only for a truly invalid item. Where this reader sees raw items in production, there is no matcher at all. So it goes, and the line now says only what this reader knows.
Reach, measured on
62ce45d140:bootStack(showcase), which has nomatchEndpoint: 0 of 2 endpoints listed and 2errorlines before; 2 of 2 and none after.os serve(aMetadataPluginsince PR fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once #23078): not reached. After the fix: 2 of 2,GET …/tasksanswers200, and noerrorline. Triage's pin holds there as a CONTROL.?preview=draft: does not reach this reader. The_draftpin is robustness only.Pins: two decorated valid items (
_diagnostics,_draft) are documented with noerrorline._packageIdparses as before.RestServerGET /openapi.jsonwith no matcher wired.Out-of-scope notes:
bootStackcomposes noMetadataPlugin, so a declaredapisroute answers404there whileos serveof the same stack answers200). The seat files it separately, through the filing gate.Changeset
.changeset/23083-openapi-read-decorations.md:@objectstack/restpatch,Clause-②: no. A valid declaration is documented, and the closed schema is unchanged. No contract-review face.Tests and gates at
76f507c0ee:@objectstack/rest: 275 files, 5758 tests.CI on
76f507c0eeat this stamp: latest run per name, 24 success and 6 skipped, with 8 in progress. Not green yet.Writes:
mcp_calls0;api_writes2 relay strokes (pr_createwith its assignee, and the report).Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Landed: PR #23087 →
0511341408(Fixes #23083). The card closescompleteddomain:cliseat 2 (#22648) ·os-steve· sessionsession_01VoSxBQujKLZKPwK2u5ehQ6· 2026-10-12T21:40Z · holder of claim6130053475, released by this landing.0511341408, a squash with one parent.selectDocumentableEndpointsinpackages/rest/src/openapi-endpoints.tsnow parsesstripReadDecorations(item),@objectstack/spec/kernel's own strip ofMETADATA_READ_DECORATIONS, which is the The route-level seed apply cannot consume the shipping protocol's own read-back envelope:_packageId/_diagnosticssurvive the unwrap andSeedLoaderRequestSchemarejects them asunrecognized_keys#15591 shape.6131098498. No contract-review face (Clause-②: no). Every check on head76f507c0eewas success or a roster skip.bootStack(showcase)went from 0 of 2 declared endpoints listed, with 2errorlines, to 2 of 2 with none.os serve(aMetadataPluginsince PR fix(cli,runtime): a host-config os serve composes a MetadataPlugin, so its declared apis are served; a matcher-less composition warns once #23078) was not reached. Triage's pin holds there as a CONTROL.apisanswer 404 ENDPOINT_NOT_FOUND there whileos serveof the same stack answers them — its "what serve composes" rule (#22301, ruling A) has a gap #23088 (finding, awaiting triage):bootStackcomposes noMetadataPlugin, so a declaredapisroute answers404there whileos serveanswers200. The document now lists those two routes onbootStack, through the stored-items fallback, which is still announced once aterror.openapi-endpoints.ts(selectDocumentableEndpoints) and its new test.Generated by Claude Code