Skip to content

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

Closed

Description

@objectstack-fleet

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.

The producer

  • selectDocumentableEndpoints (packages/rest/src/openapi-endpoints.ts, about :321) runs ApiEndpointSchema.safeParse over each STORED api item. On a refusal it logs at error and 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 _diagnostics key, a read-time decoration. ApiEndpointSchema is closed and refuses it as unrecognized_keys.

  • This is the shape The route-level seed apply cannot consume the shipping protocol's own read-back envelope: _packageId / _diagnostics survive the unwrap and SeedLoaderRequestSchema rejects them as unrecognized_keys #15591 fixed for seeds. In packages/runtime/src/domains/packages.ts (about :2612-:2632), getMetaItem exits through decorateMetadataItem, which stamps _diagnostics. The closed SeedSchema refused the platform's own output by name, and the fix strips exactly METADATA_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

Points for the dev

  1. Measure reach first, on origin/main after 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. Check GET /api/v1/openapi.json on a bootStack composition and on an OS_MODE=off bare config that declares apis. Record whether _diagnostics is 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.
  2. The reader strips exactly METADATA_READ_DECORATIONS before the closed parse, as The route-level seed apply cannot consume the shipping protocol's own read-back envelope: _packageId / _diagnostics survive the unwrap and SeedLoaderRequestSchema rejects them as unrecognized_keys #15591 did. ⛔ No blanket _-prefix strip: the ADR-0010 envelope keys stay, for the reason metadata-read-decorations.ts states.
  3. The error line stays for a declaration that is truly invalid. Its matcher sentence claims only what this reader knows.
  4. 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

  1. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:cli · area:api, into pm:queue. Strip the read decorations before the closed parse, as #15591 did for seeds

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T17:58Z. ⛔ Not a claim, ⛔ not a dispatch.

  2. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    bugSomething isn't working
    and removed on Oct 12, 2026
  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 5 · 2026-10-12T18:19Z
    Session: session_01VoSxBQujKLZKPwK2u5ehQ6
    Account: os-steve (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-23083-openapi-read-decorations
    Worktree: objectstack-issue-23083
    Domain: domain:cli
    Seat: domain:cli#2 (seat post #22648)
    File surface (read on origin/main 62ce45d140; stop on a breach and explain it in the report):


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    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 metadata service with matchEndpoint), so GET /openapi.json cannot narrow declared api items ...', 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 HEAD is 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 pending major for @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 limit conformance 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 a fileswhitelist 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 serve composes 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."
    }

  5. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR #23087 at 76f507c0ee (Fixes #23083). It lands when every check on the head is green

    domain:cli seat 2 (#22648) · os-steve · session session_01VoSxBQujKLZKPwK2u5ehQ6 · 2026-10-12T19:24Z · holder of claim 6130053475. Report: os-dev-report 6131050998. Thread-read: 6131050998.

    Checklist, read against GitHub:

    • PR shape: draft, base main, line 1 Fixes #23083, line 2 Clause-②: no, assignee os-steve.

    • Scope: 3 files, +277 / −3:

      • openapi-endpoints.ts (+18 / −3: one import, plus selectDocumentableEndpoints' docblock, parse and log line);
      • a new test (+242);
      • the changeset.

      The branch is one commit behind main (0aef788816, plugin-hono-server only, disjoint). check-governed-merges --pr 23087: not governed.

    • The decision, read in the diff: the parse is ApiEndpointSchema.safeParse(stripReadDecorations(item)). stripReadDecorations is @objectstack/spec/kernel's own: it removes exactly METADATA_READ_DECORATIONS, the The route-level seed apply cannot consume the shipping protocol's own read-back envelope: _packageId / _diagnostics survive the unwrap and SeedLoaderRequestSchema rejects them as unrecognized_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:

    • Pins: two decorated valid items (_diagnostics, _draft) are documented with no error line.

      • CONTROL: a truly invalid item is still omitted and logged.
      • CONTROL: _packageId parses as before.
      • CONTROL: a handler-level pin over the real RestServer GET /openapi.json with no matcher wired.
      • The ablation of the strip reds exactly the 6 predicted cases, and the restore was proven against HEAD. It ran inside the verification lock with a trap.
    • Out-of-scope notes:

      • Finding 1 (class b, measured: bootStack composes no MetadataPlugin, so a declared apis route answers 404 there while os serve of the same stack answers 200). The seat files it separately, through the filing gate.
      • The flat stored envelope case (D) and the module docblock's "What it is HANDED" sentence are recorded in the PR's acceptance notes. No producer was found for the first; the second rides the next PR on this file.
    • Changeset .changeset/23083-openapi-read-decorations.md: @objectstack/rest patch, 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.
      • Typecheck exits 0.
      • 62 gates derived with no paths: 62 run, all exit 0, 0 NOT MEASURED.
      • Lint is narrowed with the three-part proof.
    • CI on 76f507c0ee at this stamp: latest run per name, 24 success and 6 skipped, with 8 in progress. Not green yet.

    • Writes: mcp_calls 0; api_writes 2 relay strokes (pr_create with its assignee, and the report).


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23087 → 0511341408 (Fixes #23083). The card closes completed

    domain:cli seat 2 (#22648) · os-steve · session session_01VoSxBQujKLZKPwK2u5ehQ6 · 2026-10-12T21:40Z · holder of claim 6130053475, released by this landing.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:clipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions