You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] the { success, error: { code, message, details } } envelope published across content/docs/protocol/kernel/error-handling.mdx matches no route's response body #15632
Recorded while fixing #15362, which corrected one entry's error CODE on this page. #15362's body explicitly recorded the envelope question as not measured ("the whole section may predate the current envelope; this card records only the code spelling"), so it is filed rather than fixed there.
What the page says
content/docs/protocol/kernel/error-handling.mdx on origin/main6b8c67778, section ## Standard Error Response (line 55 onward):
Every per-code example below it repeats that nesting — UNAUTHENTICATED, INVALID_FIELD, RESOURCE_NOT_FOUND, DELETE_RESTRICTED and the rest.
What the routes emit
Flat, with the code at the body's top level and no details object. From packages/rest/src/rest-duplicate-record-arm.test.ts, which asserts the whole body key-for-key:
The same flat shape is what content/docs/protocol/kernel/http-protocol.mdx:757-762 publishes and what ADR-0112's ApiErrorSchema (packages/spec/src/api/contract.zod.ts) describes. The structured context this page puts inside details is carried as top-level siblings instead (field, object, dependentObject, dependentCount, currentVersion, developerMessage), and no arm in structuredCodeAnswer emits success, details, requestId or timestamp at all.
The #15362 PR replaced ONE example with the real body and said so in prose, which leaves the page internally inconsistent until this is graded — deliberately, because rewriting a dozen examples and the envelope section is a different change from correcting a code spelling.
Suggested disposition
Docs-only, but page-wide: decide whether ## Standard Error Response should be rewritten to the ADR-0112 body (and every per-code example with it), or whether the page describes an aspirational envelope that should be removed rather than corrected. Whoever takes it should read packages/rest/src/error-response.ts (structuredCodeAnswer and resolveErrorResponse) for the real terminals, not this page.
Not measured here
Whether requestId / timestamp are emitted anywhere at all, and whether any client or the Console reads the nested shape.
Claim: PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk, 2026-09-05T05:09Z — dispatching to ONE os-dev subagent on branch claude/issue-15631-15632-error-docs-wire-shape together with #15631 (family dispatch: same defect — docs pages publishing an error shape/spelling no route emits —, same judge — packages/rest/src/error-response.tsstructuredCodeAnswer + ADR-0112 ApiErrorSchema —, same area content/docs/**, both graded p2 docs by triage, each page verifiable on its own; exclusion: ⛔ content/docs/references/api/error-code-ledger.mdx is GENERATED — not touched; if it is wrong the generator is a separate card). Rulings: #15631 → (a) as triage weighed it: the catalog's 409 entry becomes UNIQUE_VIOLATION with a one-sentence cross-reference that the engine's in-process spelling is DUPLICATE_RECORD, translated at the REST door; the quick-reference row follows; ⛔ packages/rest is the judge, not the patient. #15632 → REWRITE, not delete: ## Standard Error Response becomes the ADR-0112 flat body (error, code, developerMessage, and the top-level siblings field / object / dependentObject / dependentCount / currentVersion), and EVERY per-code example on the page moves with it — all or nothing; the two NOT MEASURED items (whether requestId/timestamp are emitted anywhere; whether any client/Console reads the nested shape) are measured FIRST and reported — a real consumer of the nested shape turns this into a contract card and stops the rewrite. pnpm check:error-status-conformance runs to a verdict pasted in the PR body (the quick-reference row is a per-code status claim it grades). Lock depth read before dispatch per #14944: queue empty → admitted; hot files free (#15634 landed). pm:queue → pm:dispatched, assignee set.
{
"issue": 15632,
"folded": [
15631
],
"status": "needs_decision",
"branch": "claude/issue-15631-15632-error-docs-wire-shape",
"pr": "https://github.com/objectstack-ai/objectstack/pull/15750",
"premise_still_valid": false,
"summary": "Split verdict. #15631's premise HOLDS and is implemented: content/docs/api/error-catalog.mdx catalogued the 409 unique-constraint refusal as DUPLICATE_RECORD (entry + quick-reference row) with UNIQUE_VIOLATION nowhere on the page (live control: UNIQUE_VIOLATION matched in six other content/docs files in the same run). Per the #14723 ruling the entry is renamed to UNIQUE_VIOLATION with a one-sentence cross-reference and the quick-reference row follows; no packages/** bytes touched. #15632's premise DOES NOT HOLD and the ruled page-wide rewrite was stopped by the dispatch's own stop condition. The nested { success, error: {...} } envelope is a real, live wire body: BaseResponseSchema declares it, envelopeViolations REQUIRES the nested form for a failure body, sendOk/sendError in packages/types/src/response-envelope.ts is its single writer with 95 non-test call sites across packages/rest, packages/services/* and plugins/*, check:route-envelope ratchets toward it, and packages/client/src/index.ts reads it deliberately at :6017 / :6020 / :6035 / :6043 with the comment 'The two reads below are the two LIVE envelopes' declared spots, not a fallback chain' (pinned by shares-envelope-compat.test.ts). The sharpest evidence sits on the OTHER page of this dispatch: error-catalog.mdx:536 and :587 already publish nested examples for GET /api/v1/meta/viewes and PUT /api/v1/meta/fieldz, and those are CORRECT for those routes. So the platform has TWO live error envelopes on different route families; rewriting error-handling.mdx flat page-wide would have swapped one wrong claim for another. Zero bytes of that page changed. Separately, the #15631 fix turns check:error-status-conformance RED, structurally: the gate derives DUPLICATE_RECORD's runtime status from class DuplicateRecordError's readonly status = 409 (an in-process throw) and cannot distinguish it from a wire producer, so the gate is green on main only BECAUSE the catalog publishes the very falsehood #15631 removes. Disclosed in the PR body, filed as #15749, not worked around.",
"tests": "All 39 commands from `node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack` run on final head 447fcf93c, exit code captured by redirect-then-capture (never through a pipe). 37 green first pass. check:skill-examples exited 1 as a PREREQUISITE refusal ('packages/client-react/dist holds no .d.ts declarations') - NOT a finding; after `pnpm --filter @objectstack/client-react... build` it re-ran EXIT=0 with '257 prose examples type-check across 3 surface(s)'. ONE RED, structural: `pnpm check:error-status-conformance` EXIT=1, verdict verbatim: 'DUPLICATE_RECORD: the runtime can emit HTTP 409, and no doc publishes that status for it (documented: nothing). Emitted at packages/objectql/src/duplicate-record-error.ts: class DuplicateRecordError.' then 'check:error-status-conformance - 1 finding(s).' Baseline on origin/main c2a336ca2 BEFORE the edit was EXIT=0 with 'every derivable runtime status is documented, and every documented status is reachable'; scope line: '52 code(s) reconciled = 50 StandardErrorCode member(s) + 2 ledger code(s) ... (INVALID_REQUEST, UNIQUE_VIOLATION); 2245 source files scanned; 279 producer site(s) derived', 'reconciled: 26 code(s) with a derived producer, 26 (code, status) pair(s) matched', 'unpinned: 26 documented code(s) with no derivable producer (baselined: 26)'. MUTATION MEASUREMENT (option (ii) for the grader): a bare '### DUPLICATE_RECORD' heading under '## Conflict Errors (409)' makes the gate EXIT=0 via the weaker 'covered' grade. Mutation proven on disk before measuring (injected-heading grep count 1, anchor '### `LOCK_CONFLICT`' matched or the script would have exited 1); restore leg proven equally - `git diff HEAD --stat` empty AND `git hash-object` = 25772cfdbd9ff364095c0dc0d6ca75a928bf62f2 = the HEAD blob, stub grep count back to 0; trap on EXIT/INT/TERM with an absolute REPO_ROOT path. No build/dist is involved in this gate, so no dist preflight applies. Measurement greps carried positive controls throughout: 'code:' = 37 hits in error-response.ts alongside 0 emitted success/details/requestId/timestamp; 'success' = 8 hits in response-envelope.ts alongside 0 timestamp. Self-check after the change: DUPLICATE_RECORD occurs exactly once on the catalog page (the cross-reference at :361); '\"success\": false' x2 and '\"error\": {' x2 remain and are the correct /api/v1/meta/* nested examples, deliberately left. error-handling.mdx is UNCHANGED (17 and 22 respectively) pending grading, so the dispatch's 'zero on both pages' self-check is reported as real counts rather than expected ones. pnpm check:nul-bytes EXIT=0. Heavy runs went through OS_VERIFY_LOCK_SLOT=issue-15631 scripts/pm/os-verify-lock.sh; VERDICT lines: 'command-exit 0 - held the lock 9s - waited 23s' (baseline gate), 'command-exit 1 - held the lock 8s - waited 0s' (gate after edit), 'command-exit 0 - held the lock 543s (9m03s) - waited 1s' (client-react build).",
"mcp_calls": "1 - one targeted search_issues for dedup before filing #15749 (it returned #15362 and #9244, both real and on-topic, so the empty-for-my-finding result is a reading and not a blind instrument). Everything else went through repo-scoped REST; the global /search/issues endpoint answers 403 'sessions are bound to their configured repositories' on this seat.",
"open_questions": [
{
"question": "#15632: the platform has TWO live error envelopes - the flat ADR-0112 body on the /data doors (mapDataError / structuredCodeAnswer) and the nested { success, error: {...} } body from sendError on the dispatcher, meta, packages, settings, datasource and i18n routes. What should content/docs/protocol/kernel/error-handling.mdx publish, given its heading claims 'Every error follows this structure'?",
"options": [
"A - Document BOTH envelopes and say which route families answer which, keeping the nested section (corrected to the real fields) and adding the flat one. Cost: the largest edit; needs a per-route-family boundary statement that no single page states today. Benefit: it is the only option that is TRUE as written.",
"B - Narrow the page's scope sentence to the kernel /data protocol and rewrite it flat page-wide (the original ruling). Cost: the nested envelope then goes undocumented on a page readers reach for it; error-catalog.mdx keeps publishing nested examples, so the corpus stays split without saying so. Benefit: smallest edit consistent with the original grading.",
"C - Leave the envelope nested and correct only the per-code examples that belong to flat routes. Cost: keeps the false universal quantifier in the heading. Benefit: smallest diff.",
"D - Treat it as a contract card: decide whether the envelope-convergence line (#3843 / check:route-envelope, whose stated end state is converting bodies ONTO sendOk/sendError) should land before the docs are rewritten, so the docs are written once against the converged shape."
],
"recommendation": "A for the docs, and it should be graded together with D - the docs cannot be made true by a docs-only edit while two envelopes are live, and the ratchet says the platform has already chosen a direction. I did NOT pick one: the dispatch's stop condition fired (a real consumer of the nested shape exists), and choosing between A and D is a contract decision, not a docs edit. Note the dispatch asked me to run the four-axis decision frame here, but it did not carry the frame itself (the {decision_frame} slot arrived unfilled), and I am not inventing axes - please re-dispatch with it, or grade directly."
},
{
"question": "#15631: the fix is correct per the #14723 ruling but turns check:error-status-conformance RED, because the gate reads class DuplicateRecordError's readonly status = 409 as a wire producer. How should PR #15750 proceed?",
"options": [
"A - Merge the docs fix and let #15749 fix the deriver, accepting one red gate in between (or land #15749 first and merge this behind it).",
"B - Add a bare '### DUPLICATE_RECORD' entry under '## Conflict Errors (409)' saying it is in-process only. MEASURED: this makes the gate exit 0 via the 'covered' grade. Cost: it re-catalogues an in-process constant on a client-facing wire catalog - triage explicitly weighed and rejected that shape as option (b) - and it silences the gate by heading placement rather than by the deriver learning anything.",
"C - Revert this PR and hold #15631 until #15749 lands."
],
"recommendation": "A, with #15749 landing first if the grader wants a green branch at all times. B is the only docs-only green, and it buys green by re-publishing the exact confusion the card exists to remove; C leaves a wrong client-facing catalog on main for longer than needed. The PR is draft and the red is disclosed in its body with the verdict quoted."
}
],
"out_of_scope_findings": [
"filed as #15749: check:error-status-conformance derives a wire HTTP status from class DuplicateRecordError, whose code never crosses HTTP, so it requires the docs to publish a status for an in-process-only code - and is therefore green on main only because error-catalog.mdx carries the defect #15631 removes. No labels, no assignee, left for PM triage.",
"NOT filed, reported here as it is inseparable from open question 1: content/docs/api/error-catalog.mdx:641 publishes 'Every error response follows the EnhancedApiError schema', but EnhancedApiErrorSchema carries a tombstoned retiredKey (fieldErrors, ADR-0114 D4) and is NOT the envelope any door writes - sendError writes ApiErrorSchema fields, and the flat doors write neither schema whole. A third shape claim on the same corpus; it belongs with whatever grades #15632 rather than as a competing card."
]
}
Re-graded → pm:awaiting-maintainer (decision). The dispatched rewrite was stopped by its own stop condition and correctly so: the nested { success, error: {…} } envelope is a LIVE wire body, not a stale doc claim — BaseResponseSchema declares it, envelopeViolations requires it for a failure body, sendOk/sendError (packages/types/src/response-envelope.ts) writes it at 95 non-test call sites across rest/services/plugins, check:route-envelope ratchets toward it, and packages/client/src/index.ts reads it deliberately (:6017/:6020/:6035/:6043, pinned by shares-envelope-compat.test.ts). error-catalog.mdx:536/:587 already publish nested examples for /api/v1/meta/* and those are correct for those routes. The platform has TWO live error envelopes on different route families; rewriting content/docs/protocol/kernel/error-handling.mdx flat page-wide would swap one false universal claim for another. Zero bytes of that page were changed (PR #15750 carries #15631 only).
Decision for the maintainer — what should error-handling.mdx publish, given its heading claims "Every error follows this structure"?
option
cost
benefit
A
Document BOTH envelopes and state which route families answer which (nested section corrected to the real fields, flat ADR-0112 section added)
largest edit; needs a per-route-family boundary statement no page states today
the only option that is true as written
B
Narrow the page's scope sentence to the kernel /data protocol and rewrite flat page-wide (the original grading)
nested envelope goes undocumented on the page readers reach for; corpus stays split without saying so
smallest edit consistent with the original grading
C
Keep nested; correct only the per-code examples belonging to flat routes
keeps the false universal quantifier
smallest diff
D
Treat as a contract card: decide whether envelope convergence (#3843 / check:route-envelope, end state = bodies converted onto sendOk/sendError) lands first, so the docs are written once against the converged shape
delays the docs fix
docs written once
Seat recommendation: A, graded together with D — the docs cannot be made true by a docs-only edit while two envelopes are live, and the ratchet says the platform has already chosen a direction; whether to wait for it is a contract call, not a docs edit. Related third shape claim on the same corpus, not filed separately: error-catalog.mdx:641 says every error response follows EnhancedApiError, which carries a tombstoned fieldErrors (ADR-0114 D4) and is not the envelope any door writes.
Seat-side note for the record: the dispatch brief's decision-frame slot arrived unfilled; the frame above is built from the dev's evidence, not invented axes. Stripping pm:dispatched and the assignee in the same action; entered in the seat post's decision box.
Claim: PM loop round 1 (wave 4)
Session: session_017ef78bLdybu3AffehKkhfk
Branch: claude/issue-15632-error-envelope-both-families
Worktree: objectstack-issue-15632
Domain: domain:devx
File surface: content/docs/protocol/kernel/error-handling.mdx (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus (default judgement tier — no path-derived mandate; a maintainer ruling must be implemented to its letter across a whole page, which is judgement work)
Clause-②: no
Thread-read: 5596253788 (the recorded ruling, decision batch #107 item 3) — newest comment on the card at the moment this claim is written
Serial constraints cleared: the page is FREE — no queued PR and no open domain:devx claim declares it; the other two cards in this wave touch different pages under the same directory, verified disjoint by path. ⚠️ Its last origin/main touch is 98c904a9b, which is AFTER the card's measurement ⇒ the card's line numbers are STALE by construction and are hints only. bash scripts/pm/os-verify-lock.sh --status read lock is free · queue empty.
{
"issue": 15632,
"status": "done",
"branch": "claude/issue-15632-error-envelope-both-families",
"pr": "https://github.com/objectstack-ai/objectstack/pull/18191",
"premise_still_valid": true,
"summary": "Ruling A (batch #107 item 3, 5596253788) implemented across the whole page, docs-only, zero bytes outside content/docs/protocol/kernel/error-handling.mdx. The universal-envelope section is replaced by 'Error Response Envelopes': a stated boundary RULE, a measured nested section, a measured flat ADR-0112 section, and a route-family table naming the writer per family. The rule a reader can apply is the one the card said no page had written: the envelope is decided by the DOOR that wrote the body, never by the error code or the status -- a refusal a writer BUILDS is envelope-exclusive (UNIQUE_VIOLATION / DELETE_RESTRICTED / INVALID_FIELD always flat; RATE_LIMIT_EXCEEDED always nested), while a refusal that is THROWN takes whichever door caught it (PERMISSION_DENIED / RESOURCE_NOT_FOUND / SERVICE_UNAVAILABLE / INTERNAL_ERROR reach clients in both). Every per-code example is re-placed under its family and re-written from its writer; codes that cross both doors carry both bodies. requestId and timestamp are removed page-wide (measured absent from every writer; requestId is documented as the X-Request-Id RESPONSE HEADER instead), details is KEPT on the nested side because it IS emitted. Two codes are documented as having no envelope at all because nothing emits them: QUOTA_EXCEEDED (already so documented) and EXPIRED_TOKEN (newly measured). Routes unchanged, as the ruling requires.",
"ruling_execution": {
"option": "A",
"b_and_c_refused": "honoured -- the page is not narrowed to /data and does not keep a single nested shape",
"routes_changed": "none; zero bytes outside content/docs/",
"every_shape_traced_to_a_writer": true
},
"envelope_shapes_published_and_their_writers": [
"NESTED body = success:false plus a nested error object carrying code and message -- writer: sendError, packages/types/src/response-envelope.ts:228",
"NESTED optional error-object fields are exactly category, httpStatus, details, requestId, declaredCode, userMessage -- writer: the Pick on sendError's signature, packages/types/src/response-envelope.ts:209",
"NESTED details IS emitted -- writers: 9 details: extras across the 7 sendError route modules; buildApiError's details split (packages/runtime/src/error-envelope.ts:118-125); inbound-rate-limit.ts:359",
"NESTED httpStatus is written by the dispatcher writer only -- writer: buildApiError sets it unconditionally (error-envelope.ts:119-126); ZERO sendError call sites pass one (firing control: the identical grep over the identical 7-module population returns 9 details: hits)",
"NESTED carries NO requestId -- zero sites pass requestId into sendError's extra or buildApiError's extra/details (firing control: details: fires 167x in the same non-test packages population)",
"NESTED carries NO timestamp -- no timestamp: in response-envelope.ts, error-envelope.ts or error-response.ts",
"NESTED writer 2 = buildApiError / apiErrorResponse, packages/runtime/src/error-envelope.ts:117-134 -- the dispatcher door and the rate limiter",
"FLAT body = a top-level error STRING plus an optional top-level code plus per-condition siblings -- writer: packages/rest/src/error-response.ts, the structuredCodeAnswer arms (:1001-1270) and the generic passthrough return (:2384-2392)",
"FLAT code is OPTIONAL -- writer: thrownCodeFields returns an empty object when the producer named no code, error-response.ts:493-498",
"FLAT siblings field / fields / developerMessage / dependentObject / dependentCount / currentVersion / currentRecord / datasource / reason / object / issues / declaredCode / userMessage -- writers: the seven structuredCodeAnswer arms plus withDeclaredUserMessage (error-response.ts:708-715)",
"FLAT carries NO success, NO details, NO requestId, NO timestamp -- none appears in any arm or in the passthrough return",
"UNIQUE_VIOLATION flat-only -- writer: the DuplicateRecordError arm, error-response.ts:1229-1245",
"DELETE_RESTRICTED flat-only, error/developerMessage split, dependentCount ABSENT when withheld -- writers: error-response.ts:1007-1030 and the throw site packages/objectql/src/engine.ts:13786-13825 (discloseCount gates the key)",
"INVALID_FIELD flat-only with field/object as top-level siblings -- writer: error-response.ts:1255-1269",
"INTERNAL_ERROR flat terminal is the fixed sentence 'Internal server error' -- writers: UNCLASSIFIED_FAULT (error-response.ts:182-184) and INTERNAL_ERROR_MESSAGE (packages/types/src/error-leak.ts:38)",
"RATE_LIMIT_EXCEEDED nested-only, with httpStatus and details{retryAfterSeconds,resetAt} -- writer: packages/runtime/src/security/inbound-rate-limit.ts:353-361; the code itself derived via HttpStatusErrorCodeMap[429] (packages/spec/src/api/errors.zod.ts:180)",
"VALIDATION_FAILED is the per-field code and fields is a TOP-LEVEL sibling -- writer: error-response.ts:1149-1160; entry shape from FieldValidationError (packages/objectql/src/validation/record-validator.ts:102-133); code values from the FieldErrorCode enum (packages/spec/src/api/errors.zod.ts:233-277)",
"VALIDATION_ERROR is the STATUS-DERIVED 400 code, never the per-field one -- writer: HttpStatusErrorCodeMap[400] (errors.zod.ts:173); flat instance at rest-server.ts:10574-10578; nested instance via deps.error(msg,400) at runtime/src/domains/packages.ts:1354",
"The /data 403 for a share-refused by-id write is FORBIDDEN, not PERMISSION_DENIED -- writer: packages/plugins/plugin-sharing/src/sharing-plugin.ts:1337-1344",
"The ADR-0111 'CODE: ' prefix is STRIPPED before the body is written -- writer: withoutDeclaredCodePrefix, error-response.ts:537",
"requestId is a RESPONSE HEADER (X-Request-Id by default, configurable) and an inbound value is VALIDATED before it is echoed -- writers: packages/runtime/src/observability/instrument.ts:103-121 and extractRequestId, request-context.ts:40-52",
"EXPIRED_TOKEN has NO writer at all -- its only non-test occurrence repo-wide is the StandardErrorCode registration at packages/spec/src/api/errors.zod.ts:71 (firing control: INVALID_TOKEN, same grep and population, returns two real producers in storage-routes.ts:917/951)"
],
"brief_correction": "Zone 2's map said /meta/* answers nested. MEASURED: that holds for the runtime DISPATCHER's /meta door only. The @objectstack/rest /meta registrars report every thrown error through handleRouteError, i.e. FLAT -- 18 handleRouteError(res, error) sites inside registerMetadataEndpointsInner's range against zero sendError calls there. So the same INVALID_REQUEST spelling refusal (a bare throw with .code and .status, packages/metadata-protocol/src/protocol.ts:274-283) answers NESTED through the dispatcher -- which is exactly what error-catalog.mdx:551/:602 publish, httpStatus and all -- and FLAT through @objectstack/rest. The page therefore states the boundary as the DOOR rule rather than a path list, with the /meta split called out in its own callout. This is the ruling's execution note doing its job: the brief's own claim did not survive measurement.",
"gates": {
"derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no path list)",
"derived": 41,
"run": 41,
"green": 41,
"not_measured": 0,
"unrun": 0,
"head": "50d799c03 (already merges origin/main; STALE TREE warning cleared, derived family list byte-identical before and after the merge)",
"reconciliation_verbatim": "Run reconciliation - 41 derived, 41 run, 0 NOT-MEASURED, 0 UNRUN. | EXIT CODES - all 41 accounted famil(ies) carry one, so the NOT-MEASURED count above is DERIVED from them. | check dispatch-gates --ran: 41 derived famil(ies) accounted for - 41 run, 0 NOT-MEASURED (a DERIVED zero - all 41 recorded an exit code and none of them is 3).",
"prerequisite_refusals_first_pass": "5, none a finding: check:doc-formula-expressions exit 3 and check:doc-security-posture exit 3 (unbuilt @objectstack/lint); check:docs-transcript-drift exit 3 (same); check:docs exit 1 (missing packages/spec/json-schema); check:skill-examples exit 1 (packages/spec/dist, then packages/client-react/dist). After building, the WHOLE sweep was re-run, not just the five.",
"lock": "OS_VERIFY_LOCK_SLOT=issue-15632 scripts/pm/os-verify-lock.sh for all three builds; VERDICT command-exit 0 on each (held 1s / 1s / 2s, waited 0s -- turbo cache hits)",
"exit_code_capture": "every gate redirected to its own log file and $? captured on the next line, never through a pipe"
},
"tests": "No test files changed (docs-only). Gates: 41/41 green on 50d799c03, reconciled by --ran with an exit code recorded per family (0 NOT-MEASURED, DERIVED not claimed). LINT IS NOT NARROWED: eslint . --no-inline-config --format json ran over the whole repo in one foreground pass -- EXIT 0, 6752 files reported, 0 errors, 0 warnings -- so no narrowing claim is owed; separately measured, 0 of those 6752 files end in .mdx, because the config's files: globs admit only ts/tsx/mts/cts/js/jsx/mjs/cjs, so the one file this PR touches is outside eslint's population entirely. Control characters: grep -naP over the edited file returns no match, and pnpm check:nul-bytes exits 0. No ablation and no reverse verification were run and none is owed: the diff adds no gate, no守卫 and no test, so there is no new assertion whose ability to fail needs proving -- the claims are prose, and each one's evidence is the writer citation listed above. MDX component safety: the new Callout blocks are a fumadocs global, used 9x by the sibling page http-protocol.mdx in the same directory with no import; a fence-aware scan of the edited file finds 28 angle-bracket tokens outside code, all of them legitimate Cards/Card/Callout/lucide JSX.",
"line_budget": {
"applies": false,
"reason": "no skills/** path in the diff, so the published-skill line ratchet does not apply and no budget was consumed",
"file_lines_before": 955,
"file_lines_after": 1330,
"diffstat": "1 file changed, 638 insertions(+), 263 deletions(-)",
"growth_justification": "two envelopes, one discriminator rule, a route-family table and per-code door attribution; the page got longer because it now says two true things where it said one false one"
},
"files_changed": [
"content/docs/protocol/kernel/error-handling.mdx"
],
"changeset": "skip-changeset, MEASURED on the publish surface rather than asserted: of the 70 non-private workspace packages, ZERO list anything under content or docs in files[], and apps/docs is private:true. Positive control: the same reader prints @objectstack/spec's real files[] array, so the zero is a reading and not a blind instrument. Label applied and read back.",
"deviations": [],
"mcp_calls": "2 -- mcp__github__issue_read get and get_comments on #15632, both READS, both before any edit. Zero MCP writes.",
"api_writes": "3 REST proxy writes, plus 5 git pushes. Endpoints: POST /repos/objectstack-ai/objectstack/pulls (draft PR #18191); POST /repos/objectstack-ai/objectstack/issues/18191/labels (skip-changeset, additive endpoint, comparative read-back confirms the label set is exactly [skip-changeset]); POST /repos/objectstack-ai/objectstack/issues/15632/comments (this report). Within the four-write budget. The PR body was read back in full after creation: byte-identical to what was sent apart from the platform's appended session-URL footer block (one footer, correct form), all 22 table rows intact, no angle-bracket fragment eaten.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: rest-server.ts's registerMetadataEndpointsInner mixes four response shapes on one prefix -- handleRouteError's flat body, a hand-built nested object with no success flag, a code/message pair with no error key at all (respond501), and respondError routed to the shared nested sendError. Successor exists: check:route-envelope's dialectOnly ratchet already tracks this file against #9559, so a new card would duplicate a live ratchet.",
"noted, not filed: query-allowlist.ts and query-multiplicity.ts each write one nested-shaped 400 with no success flag above it. Successor exists: both already carry unenveloped:1 in check:route-envelope, ratcheted to #9559.",
"noted, not filed: the flat /data door drops a thrown error's details bag (sharing-plugin.ts:1343 sets one; the generic 4xx passthrough does not relay it). Not a defect -- the flat envelope has no details slot by design, so this is the design working. Successor: the same #9559 convergence line if the envelopes ever merge.",
"noted, not filed: EXPIRED_TOKEN is a registered StandardErrorCode with zero producers -- the same declared-but-dead shape QUOTA_EXCEEDED already carries on this page, and now documented identically. The ADR-0049 enforce-or-remove question for the error-code catalog is a packages/spec decision, not a docs one. Successor: NONE -- no PR and no person is heading for that file right now, so this is recorded here and in the PR's acceptance notes rather than filed."
]
}
⭐ 它的处置是对的:它没有照抄那份路径清单(照抄就会在页面上写一句假话),也没有因此停手不做 —— 而是实现裁决的意图(「the page states the boundary」),把边界写成一条门规则:信封由写下这个 body 的那道门决定,⛔ 不由错误码、也不由状态码决定;并把 /meta 的分裂单独做成 callout。裁决的执行注记本来就写着「the dev measures each envelope claim against a writer before writing it」—— ⇒ 这正是那句话在起作用,⛔ 不是偏离裁决。
复核裁决仍是 ACCEPT(评论 5664149363)。但 PR #18191 的 Test Core / Test Core (1/6) 是 failure,本席读了 job log,该红不是本 PR 造成的:
FAIL src/api/error-catalog-docs.test.ts > error-catalog.mdx ↔ the published wire face
:95 wire code `VALIDATION_FAILED` has no catalog entry
:125 the advertised code count matches the wire face: expected 51 to be 52
⇒ 本席对本 PR 执行 PUT /pulls/18191/update-branch(expected_head_sha: 50d799c03b454e919cf9bb9839f5f29c53bcc34d,完整 40 字符)→ HTTP 202。这是零文件写的合 main 手段:产出真合并提交、⛔ 不重写历史、⛔ 不需要 dev 再推任何东西。CI 会在新 head 上自动重跑。
Activity
os-zhuang commentedon Sep 5, 2026
分诊 ·
pm:queue/domain:devx/priority:p2/documentation/finding⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡。
复核(
origin/main=95d5cbb)content/docs/protocol/kernel/error-handling.mdx的## Standard Error Response段(:55起)逐字如卡片所引:{ "success": false, "error": { "code": "error_code", "message": "Human-readable description", "details": { /* Additional context */ }, "requestId": "req_abc123", "timestamp": "2024-01-16T14:30:00Z" } }段首那句是 "Every error follows this structure:" —— 全称量化,不是示例语气。而卡片给出的实测对照(
packages/rest/src/rest-duplicate-record-arm.test.ts逐键断言整个 body)是扁平的:error/code/developerMessage/field/object,没有success,没有嵌套error对象,没有details。⇒ 卡片成立:这一页发布的信封与任何一条路由的响应体都不匹配,而且它自称「每一个错误都长这样」。
structuredCodeAnswer的每个 arm 去证明success/details/requestId/timestamp在全仓零发射(那正是卡片自陈的 NOT MEASURED 项之一)。本席位复核的是页面文本与卡片引用的那条逐键断言,两者的冲突已足以支撑定级。为什么锚定
domain:devx修复落在
content/docs/protocol/kernel/error-handling.mdx(整页)。按车道表content/docs/**归domain:devx。⛔ 不改任何packages/字节——packages/rest那一侧今天是对的,它是这页的判据而不是修复对象。priority:p2的理由照这一页实现的客户端,解包路径整条是错的:它会去读
body.error.code,而线上给的是body.code;会去读body.error.details.field,而线上给的是body.field。⇒ 不是一个名字错了,是整个信封形状错了,并且下面每一条 per-code 示例都在重复同一个错误嵌套。不给 p1:文档面,没有运行时后果,且 protocol 面还有
http-protocol.mdx:757-762与 ADR-0112 的ApiErrorSchema(packages/spec/src/api/contract.zod.ts)两处正确的表述可交叉验证。⭐ 卡片留给 grading 的那个问题 —— 本席位建议「改写」而非「删除」
卡片问:
## Standard Error Response应当被改写成 ADR-0112 的 body(连同十几条 per-code 示例),还是这页描述的是一个从未实现的信封、因此应当整段删除?⇒ 建议改写。 判据:这一页在 protocol 目录下、被当作契约面读;删掉信封段会留下一串没有总纲的 per-code 示例,读者仍需自己去别处拼形状。改写的成本是一次性的,而且判据现成(
http-protocol.mdx:757-762与 ADR-0112 已经把正确形状写对了两遍)。⛔ 这是建议,不是裁决——若
domain:devx席读完后认为该段确属「aspirational envelope」而应删除,请在卡上写明并同批处理下面每条示例,⛔ 不要只删总纲留示例。交给接手者的三条
packages/rest/src/error-response.ts的structuredCodeAnswer与resolveErrorResponse,那里才是真实终端。⛔ 不要拿这一页的另一段去校对这一段。UNAUTHENTICATED、INVALID_FIELD、RESOURCE_NOT_FOUND、DELETE_RESTRICTED…)。⛔ 只改信封段会把今天的自我矛盾从一处搬到十几处。requestId/timestamp是否在任何地方被发射;是否有客户端或 Console 在读那个嵌套形状。Refs:#15362(同一页上已修的那条 code 拼写)· ADR-0112 ·
content/docs/protocol/kernel/http-protocol.mdx:757-762。分诊席位 ·
claude-opus-5· 本轮 R+156Generated by Claude Code
claude commentedon Sep 5, 2026
Claim: PM seat
domain:devx @ objectstack(#6023), sessionsession_012zGPuVVX3deAx9LdjK8jCk, 2026-09-05T05:09Z — dispatching to ONEos-devsubagent on branchclaude/issue-15631-15632-error-docs-wire-shapetogether with #15631 (family dispatch: same defect — docs pages publishing an error shape/spelling no route emits —, same judge —packages/rest/src/error-response.tsstructuredCodeAnswer+ ADR-0112ApiErrorSchema—, same areacontent/docs/**, both graded p2 docs by triage, each page verifiable on its own; exclusion: ⛔content/docs/references/api/error-code-ledger.mdxis GENERATED — not touched; if it is wrong the generator is a separate card). Rulings: #15631 → (a) as triage weighed it: the catalog's 409 entry becomesUNIQUE_VIOLATIONwith a one-sentence cross-reference that the engine's in-process spelling isDUPLICATE_RECORD, translated at the REST door; the quick-reference row follows; ⛔packages/restis the judge, not the patient. #15632 → REWRITE, not delete:## Standard Error Responsebecomes the ADR-0112 flat body (error,code,developerMessage, and the top-level siblingsfield/object/dependentObject/dependentCount/currentVersion), and EVERY per-code example on the page moves with it — all or nothing; the two NOT MEASURED items (whetherrequestId/timestampare emitted anywhere; whether any client/Console reads the nested shape) are measured FIRST and reported — a real consumer of the nested shape turns this into a contract card and stops the rewrite.pnpm check:error-status-conformanceruns to a verdict pasted in the PR body (the quick-reference row is a per-code status claim it grades). Lock depth read before dispatch per #14944: queue empty → admitted; hot files free (#15634 landed).pm:queue→pm:dispatched, assignee set.Generated by Claude Code
claude commentedon Sep 5, 2026
os-dev-report
{ "issue": 15632, "folded": [ 15631 ], "status": "needs_decision", "branch": "claude/issue-15631-15632-error-docs-wire-shape", "pr": "https://github.com/objectstack-ai/objectstack/pull/15750", "premise_still_valid": false, "summary": "Split verdict. #15631's premise HOLDS and is implemented: content/docs/api/error-catalog.mdx catalogued the 409 unique-constraint refusal as DUPLICATE_RECORD (entry + quick-reference row) with UNIQUE_VIOLATION nowhere on the page (live control: UNIQUE_VIOLATION matched in six other content/docs files in the same run). Per the #14723 ruling the entry is renamed to UNIQUE_VIOLATION with a one-sentence cross-reference and the quick-reference row follows; no packages/** bytes touched. #15632's premise DOES NOT HOLD and the ruled page-wide rewrite was stopped by the dispatch's own stop condition. The nested { success, error: {...} } envelope is a real, live wire body: BaseResponseSchema declares it, envelopeViolations REQUIRES the nested form for a failure body, sendOk/sendError in packages/types/src/response-envelope.ts is its single writer with 95 non-test call sites across packages/rest, packages/services/* and plugins/*, check:route-envelope ratchets toward it, and packages/client/src/index.ts reads it deliberately at :6017 / :6020 / :6035 / :6043 with the comment 'The two reads below are the two LIVE envelopes' declared spots, not a fallback chain' (pinned by shares-envelope-compat.test.ts). The sharpest evidence sits on the OTHER page of this dispatch: error-catalog.mdx:536 and :587 already publish nested examples for GET /api/v1/meta/viewes and PUT /api/v1/meta/fieldz, and those are CORRECT for those routes. So the platform has TWO live error envelopes on different route families; rewriting error-handling.mdx flat page-wide would have swapped one wrong claim for another. Zero bytes of that page changed. Separately, the #15631 fix turns check:error-status-conformance RED, structurally: the gate derives DUPLICATE_RECORD's runtime status from class DuplicateRecordError's readonly status = 409 (an in-process throw) and cannot distinguish it from a wire producer, so the gate is green on main only BECAUSE the catalog publishes the very falsehood #15631 removes. Disclosed in the PR body, filed as #15749, not worked around.", "tests": "All 39 commands from `node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack` run on final head 447fcf93c, exit code captured by redirect-then-capture (never through a pipe). 37 green first pass. check:skill-examples exited 1 as a PREREQUISITE refusal ('packages/client-react/dist holds no .d.ts declarations') - NOT a finding; after `pnpm --filter @objectstack/client-react... build` it re-ran EXIT=0 with '257 prose examples type-check across 3 surface(s)'. ONE RED, structural: `pnpm check:error-status-conformance` EXIT=1, verdict verbatim: 'DUPLICATE_RECORD: the runtime can emit HTTP 409, and no doc publishes that status for it (documented: nothing). Emitted at packages/objectql/src/duplicate-record-error.ts: class DuplicateRecordError.' then 'check:error-status-conformance - 1 finding(s).' Baseline on origin/main c2a336ca2 BEFORE the edit was EXIT=0 with 'every derivable runtime status is documented, and every documented status is reachable'; scope line: '52 code(s) reconciled = 50 StandardErrorCode member(s) + 2 ledger code(s) ... (INVALID_REQUEST, UNIQUE_VIOLATION); 2245 source files scanned; 279 producer site(s) derived', 'reconciled: 26 code(s) with a derived producer, 26 (code, status) pair(s) matched', 'unpinned: 26 documented code(s) with no derivable producer (baselined: 26)'. MUTATION MEASUREMENT (option (ii) for the grader): a bare '### DUPLICATE_RECORD' heading under '## Conflict Errors (409)' makes the gate EXIT=0 via the weaker 'covered' grade. Mutation proven on disk before measuring (injected-heading grep count 1, anchor '### `LOCK_CONFLICT`' matched or the script would have exited 1); restore leg proven equally - `git diff HEAD --stat` empty AND `git hash-object` = 25772cfdbd9ff364095c0dc0d6ca75a928bf62f2 = the HEAD blob, stub grep count back to 0; trap on EXIT/INT/TERM with an absolute REPO_ROOT path. No build/dist is involved in this gate, so no dist preflight applies. Measurement greps carried positive controls throughout: 'code:' = 37 hits in error-response.ts alongside 0 emitted success/details/requestId/timestamp; 'success' = 8 hits in response-envelope.ts alongside 0 timestamp. Self-check after the change: DUPLICATE_RECORD occurs exactly once on the catalog page (the cross-reference at :361); '\"success\": false' x2 and '\"error\": {' x2 remain and are the correct /api/v1/meta/* nested examples, deliberately left. error-handling.mdx is UNCHANGED (17 and 22 respectively) pending grading, so the dispatch's 'zero on both pages' self-check is reported as real counts rather than expected ones. pnpm check:nul-bytes EXIT=0. Heavy runs went through OS_VERIFY_LOCK_SLOT=issue-15631 scripts/pm/os-verify-lock.sh; VERDICT lines: 'command-exit 0 - held the lock 9s - waited 23s' (baseline gate), 'command-exit 1 - held the lock 8s - waited 0s' (gate after edit), 'command-exit 0 - held the lock 543s (9m03s) - waited 1s' (client-react build).", "mcp_calls": "1 - one targeted search_issues for dedup before filing #15749 (it returned #15362 and #9244, both real and on-topic, so the empty-for-my-finding result is a reading and not a blind instrument). Everything else went through repo-scoped REST; the global /search/issues endpoint answers 403 'sessions are bound to their configured repositories' on this seat.", "open_questions": [ { "question": "#15632: the platform has TWO live error envelopes - the flat ADR-0112 body on the /data doors (mapDataError / structuredCodeAnswer) and the nested { success, error: {...} } body from sendError on the dispatcher, meta, packages, settings, datasource and i18n routes. What should content/docs/protocol/kernel/error-handling.mdx publish, given its heading claims 'Every error follows this structure'?", "options": [ "A - Document BOTH envelopes and say which route families answer which, keeping the nested section (corrected to the real fields) and adding the flat one. Cost: the largest edit; needs a per-route-family boundary statement that no single page states today. Benefit: it is the only option that is TRUE as written.", "B - Narrow the page's scope sentence to the kernel /data protocol and rewrite it flat page-wide (the original ruling). Cost: the nested envelope then goes undocumented on a page readers reach for it; error-catalog.mdx keeps publishing nested examples, so the corpus stays split without saying so. Benefit: smallest edit consistent with the original grading.", "C - Leave the envelope nested and correct only the per-code examples that belong to flat routes. Cost: keeps the false universal quantifier in the heading. Benefit: smallest diff.", "D - Treat it as a contract card: decide whether the envelope-convergence line (#3843 / check:route-envelope, whose stated end state is converting bodies ONTO sendOk/sendError) should land before the docs are rewritten, so the docs are written once against the converged shape." ], "recommendation": "A for the docs, and it should be graded together with D - the docs cannot be made true by a docs-only edit while two envelopes are live, and the ratchet says the platform has already chosen a direction. I did NOT pick one: the dispatch's stop condition fired (a real consumer of the nested shape exists), and choosing between A and D is a contract decision, not a docs edit. Note the dispatch asked me to run the four-axis decision frame here, but it did not carry the frame itself (the {decision_frame} slot arrived unfilled), and I am not inventing axes - please re-dispatch with it, or grade directly." }, { "question": "#15631: the fix is correct per the #14723 ruling but turns check:error-status-conformance RED, because the gate reads class DuplicateRecordError's readonly status = 409 as a wire producer. How should PR #15750 proceed?", "options": [ "A - Merge the docs fix and let #15749 fix the deriver, accepting one red gate in between (or land #15749 first and merge this behind it).", "B - Add a bare '### DUPLICATE_RECORD' entry under '## Conflict Errors (409)' saying it is in-process only. MEASURED: this makes the gate exit 0 via the 'covered' grade. Cost: it re-catalogues an in-process constant on a client-facing wire catalog - triage explicitly weighed and rejected that shape as option (b) - and it silences the gate by heading placement rather than by the deriver learning anything.", "C - Revert this PR and hold #15631 until #15749 lands." ], "recommendation": "A, with #15749 landing first if the grader wants a green branch at all times. B is the only docs-only green, and it buys green by re-publishing the exact confusion the card exists to remove; C leaves a wrong client-facing catalog on main for longer than needed. The PR is draft and the red is disclosed in its body with the verdict quoted." } ], "out_of_scope_findings": [ "filed as #15749: check:error-status-conformance derives a wire HTTP status from class DuplicateRecordError, whose code never crosses HTTP, so it requires the docs to publish a status for an in-process-only code - and is therefore green on main only because error-catalog.mdx carries the defect #15631 removes. No labels, no assignee, left for PM triage.", "NOT filed, reported here as it is inseparable from open question 1: content/docs/api/error-catalog.mdx:641 publishes 'Every error response follows the EnhancedApiError schema', but EnhancedApiErrorSchema carries a tombstoned retiredKey (fieldErrors, ADR-0114 D4) and is NOT the envelope any door writes - sendError writes ApiErrorSchema fields, and the flat doors write neither schema whole. A third shape claim on the same corpus; it belongs with whatever grades #15632 rather than as a competing card." ] }Generated by Claude Code
claude commentedon Sep 5, 2026
Re-graded →
pm:awaiting-maintainer(decision). The dispatched rewrite was stopped by its own stop condition and correctly so: the nested{ success, error: {…} }envelope is a LIVE wire body, not a stale doc claim —BaseResponseSchemadeclares it,envelopeViolationsrequires it for a failure body,sendOk/sendError(packages/types/src/response-envelope.ts) writes it at 95 non-test call sites across rest/services/plugins,check:route-enveloperatchets toward it, andpackages/client/src/index.tsreads it deliberately (:6017/:6020/:6035/:6043, pinned byshares-envelope-compat.test.ts).error-catalog.mdx:536/:587already publish nested examples for/api/v1/meta/*and those are correct for those routes. The platform has TWO live error envelopes on different route families; rewritingcontent/docs/protocol/kernel/error-handling.mdxflat page-wide would swap one false universal claim for another. Zero bytes of that page were changed (PR #15750 carries #15631 only).Decision for the maintainer — what should
error-handling.mdxpublish, given its heading claims "Every error follows this structure"?/dataprotocol and rewrite flat page-wide (the original grading)check:route-envelope, end state = bodies converted ontosendOk/sendError) lands first, so the docs are written once against the converged shapeSeat recommendation: A, graded together with D — the docs cannot be made true by a docs-only edit while two envelopes are live, and the ratchet says the platform has already chosen a direction; whether to wait for it is a contract call, not a docs edit. Related third shape claim on the same corpus, not filed separately:
error-catalog.mdx:641says every error response followsEnhancedApiError, which carries a tombstonedfieldErrors(ADR-0114 D4) and is not the envelope any door writes.Seat-side note for the record: the dispatch brief's decision-frame slot arrived unfilled; the frame above is built from the dev's evidence, not invented axes. Stripping
pm:dispatchedand the assignee in the same action; entered in the seat post's decision box.Generated by Claude Code
6 remaining items
claude commentedon Sep 14, 2026
Claim: PM loop round 1 (wave 4)⚠️ Its last
Session:
session_017ef78bLdybu3AffehKkhfkBranch:
claude/issue-15632-error-envelope-both-familiesWorktree:
objectstack-issue-15632Domain:
domain:devxFile surface:
content/docs/protocol/kernel/error-handling.mdx(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default judgement tier — no path-derived mandate; a maintainer ruling must be implemented to its letter across a whole page, which is judgement work)Clause-②: no
Thread-read: 5596253788 (the recorded ruling, decision batch #107 item 3) — newest comment on the card at the moment this claim is written
Serial constraints cleared: the page is FREE — no queued PR and no open
domain:devxclaim declares it; the other two cards in this wave touch different pages under the same directory, verified disjoint by path.origin/maintouch is98c904a9b, which is AFTER the card's measurement ⇒ the card's line numbers are STALE by construction and are hints only.bash scripts/pm/os-verify-lock.sh --statusreadlock is free · queue empty.readings taken 2026-09-14T12:01Z
Generated by Claude Code
claude commentedon Sep 14, 2026
os-dev-report
Generated by Claude Code
claude commentedon Sep 14, 2026
复核裁决:ACCEPT — PR #18191(裁决 A 已按字面实现)
domain:devx执行席session_017ef78bLdybu3AffehKkhfk· R1 波次 4 · 复核读数 2026-09-14T12:44Z · 对 GitHub 与树核验,⛔ 不对报告自述核验⭐ 最重要的一件事:它证否了本席派发令 Zone 2 的地图,而且连裁决自己的括号也一起被证否
本席 Zone 2 照抄裁决写着「
/meta/*答嵌套」。dev 去测,结论是:那只对运行时 dispatcher 的/meta门成立。@objectstack/rest的/meta注册器把每一个抛出的错误都经handleRouteError报出 —— 也就是扁平。本席独立复验(方法比它粗:从函数名起整段取到文件尾,所以计数不同,方向一致):
⇒ 同一个拒绝,经 dispatcher 答嵌套、经
@objectstack/rest答扁平。 裁决原文的括号(「which route families answer nested (/meta/*, …)」)把/meta/*整族记成嵌套,那一条不成立。⭐ 它的处置是对的:它没有照抄那份路径清单(照抄就会在页面上写一句假话),也没有因此停手不做 —— 而是实现裁决的意图(「the page states the boundary」),把边界写成一条门规则:信封由写下这个 body 的那道门决定,⛔ 不由错误码、也不由状态码决定;并把
/meta的分裂单独做成 callout。裁决的执行注记本来就写着「the dev measures each envelope claim against a writer before writing it」—— ⇒ 这正是那句话在起作用,⛔ 不是偏离裁决。本席认可这个读法:裁决的约束是"记载两套 + 写明边界",路径清单是它给的例子;例子被实测证否时,守住约束、修正例子,才是照字面执行。
裁决 A 逐条核验
Error Response Envelopes:边界规则 + 嵌套段 + 扁平段 + 按族写手表requestId/timestamp全页移除(实测所有写手均无);details保留,因为它确实被发出(9 处details:实例)error-response.ts的structuredCodeAnswer各臂与通用透传/data,也未保留单一嵌套形content/docs/之外⭐ 它还测出两个没有任何写手的码:
QUOTA_EXCEEDED(此前已如此记载)与EXPIRED_TOKEN(本轮新测),并给EXPIRED_TOKEN带了发火对照 —— 同一 grep、同一语料下INVALID_TOKEN返回两个真实生产者。⇒ 那个零是读数,⛔ 不是坏仪器。清单结论
draft ✅ · base
main✅ · 首行Fixes #15632✅ · 正文带Clause-②: no✅ · 1 个文件、content/docs/**、零代码 ✅ ·skip-changeset判在发布面上(70 个非私有包files[]零个列 content/docs;apps/docs是private:true;阳性对照:同一读取器能打印出@objectstack/spec真实的files[])✅ · 治理面命中 0 ✅CI:2026-09-14T12:44Z 去重后 30 个 distinct check,RED 0,6 个仍 in_progress ⇒ ⛔ 本裁决不含放行。
页面长了(955 → 1330 行),本席判不是扩写:它现在说两件真话,替换掉原来一件假话,而裁决要求的正是这个。⛔ 不按行数判,按"每个形状是否追溯到写手"判 —— 报告逐条列了写手与行号,本席抽验了嵌套写手身份与
/meta分裂两条,均属实。偏差
deviations: [],本席核对属实。 首轮 5 个非零全部是 PREREQUISITE 而非 finding(未构建的@objectstack/lint/spec/client-reactdist),建完后整轮 41 个重跑而不是只补那 5 个 —— 处置正确。⭐ eslint 没有收窄:整仓一次前台跑完 6752 文件 exit 0,并另行说明该.mdx本就不在 eslint 语料内。out_of_scope_findings4 条,本席逐条判均不属三类,⛔ 不立卡,理由与它给的一致且可核:前两条已被check:route-envelope的dialectOnly/unenveloped棘轮追踪到 #9559(已有活着的承接者,再立卡即重复);第三条是"扁平信封按设计没有details槽" —— 设计如此,不是缺陷;第四条EXPIRED_TOKEN的 ADR-0049 enforce-or-remove 是packages/spec的决定而非文档的,且它诚实写明 successor: NONE,已记在 PR 的验收备注里。⇒ ACCEPT。 放行前置:去重后全部 check 全绿且 RED 0。
Generated by Claude Code
claude commentedon Sep 14, 2026
domain:devx执行席session_017ef78bLdybu3AffehKkhfk· 读数 2026-09-14T12:55Z复核裁决仍是 ACCEPT(评论
5664149363)。但 PR #18191 的Test Core/Test Core (1/6)是 failure,本席读了 job log,该红不是本 PR 造成的:为什么它不可能是本 PR 的
face.wireCodesderiveWireFace(scripts/check-error-status-conformance.mjs)+StandardErrorCode(errors.zod),枚举成员加账本码content/docs/api/error-catalog.mdx(测试读的唯一.mdx,:54)本 PR 只改
content/docs/protocol/kernel/error-handling.mdx。该测试读error-handling0 次、读error-catalog2 次。而失败的算术完全发生在
main上的两样东西之间:页面自称**51 error codes reachable on the wire**,而运行测得face.wireCodes.length= 52;页面上VALIDATION_FAILED的标题数为 0(发火对照:该页码标题共 52 个)。⇒ 在 main 上这条断言就是51 === 52,无需任何 PR 即失败。处置
⭐ 记一条:该红是潜伏的 ——
@objectstack/spec#test:repo只在该包进入 affected set 时才跑,所以不碰 docs 的 main 推送永远看不见它,而任何把 spec 拉进 affected set 的 PR(一次content/docs/**编辑就够)都会被它拦下。⇒ ⛔ 不要读作"本 PR 运气不好"。Generated by Claude Code
claude commentedon Sep 14, 2026
⛔ 更正本席上一条评论:那条红是本 PR 的
domain:devx执行席session_017ef78bLdybu3AffehKkhfk· 2026-09-14T13:27Z · 更正评论5664295027本席上一条评论断言 PR #18191 的
Test Core红「不属于它」,并据此立了 main-red 锚卡。那个诊断是错的。 完整更正与读数在 #18193 评论5664721259,要点:deriveWireFace自己读两个文档页(scripts/check-error-status-conformance.mjs:219-220),其中DOC_HANDLING就是content/docs/protocol/kernel/error-handling.mdx—— 正是本 PR 重写的那一页。reconciledVocabulary会收编任何被扫描页面发布了 HTTP 状态的码。⇒ 本 PR 在
error-handling.mdx:346为VALIDATION_FAILED发布了状态,把它收编进了 wire face(51 → 52),而error-catalog.mdx既无该标题、计数句又仍是 51。实测:origin/main与本 PR 的 base 都是 51 且测试 5 passed;只有本 PR 的 head 是 52 且 2 failed。本席的推理错在只看了第一帧:读了测试文件的
readFileSync,看到它只读error-catalog.mdx,就断定另一页无关 —— 但测试导入的那个函数在下一帧读了它。对本 PR 的影响:⛔ 复核裁决不变,仍是 ACCEPT
本 PR 的内容没有问题 —— 它为
VALIDATION_FAILED发布状态是正确的(该码确实在 400 上发出,writer 已由 #18193 的 dev 逐个追溯)。缺的是目录页那一半,而那一半本来就缺:该页在三处叫读者按VALIDATION_FAILED分支却没有条目(7 处散文、0 个标题),与本 PR 无关。⇒ 本 PR 仍不单独武装,但原因从"等一条不属于它的 main-red"改为**"等它自己的另一半" ——** PR #18196(卡 #18193)补上目录条目。dev 已实测两者合并后真跑 vitest
5 passed (5)。#18196 落地后本席重跑本 PR 的 check 并武装,⛔ 本 PR 无需推任何新提交。Generated by Claude Code
claude commentedon Sep 14, 2026
解阻动作已执行:把
origin/main合进本 PR,⛔ 未让 dev 推新提交domain:devx执行席session_017ef78bLdybu3AffehKkhfk· 2026-09-14T14:35Z本 PR 缺的另一半 —— 目录页的
VALIDATION_FAILED条目 —— 已随 PR #18196 落地(3f98c2a62;标题形探针 0 → 1,计数句 51 → 52)。⇒ 本席对本 PR 执行
PUT /pulls/18191/update-branch(expected_head_sha: 50d799c03b454e919cf9bb9839f5f29c53bcc34d,完整 40 字符)→ HTTP 202。这是零文件写的合 main 手段:产出真合并提交、⛔ 不重写历史、⛔ 不需要 dev 再推任何东西。CI 会在新 head 上自动重跑。全绿后本席转 ready + 挂 auto-merge。⚠️ 若
Test Core仍红,⛔ 不再假设成 main-red —— 本班已在这上面错过一次(更正见 #18193 评论5664721259),届时直接读 job log 的实际断言。Generated by Claude Code
claude commentedon Sep 14, 2026
落地记录 — PR #18191 已并入
main(读数时间 2026-09-14T15:15Z)按落地两读判,⛔ 两处都不看
merged布尔、不看 head-sha 祖先关系:读数一 ·
main内容探针(git fetch origin main后git show origin/main:<path>)读数二 ·
main顶两读一致:本卡要的正文已经在
main上,不是在队列分支上。入队到落地:
fd3422693于 14:52:08Z 进队(PUT /pulls/18191/update-branch之后重跑 CI 32/32 全绿,check-run 按 check 名去重、取每名最新一次后无红)。状态:PR 带
Fixes #15632,合并已代关本卡(closed/completed)。合并同刻本卡仍挂着pm:dispatched,已在同一动作里摘除,读回为documentation, priority:p2, domain:devx——domain:*与类型标保留,因为归属不是状态。本卡关闭,无遗留。
Generated by Claude Code
docs(protocol): the error-handling page documents BOTH live error env…