Packet — R93 B3 Document Dual Error-Envelope Pattern in s17
Round: R93 debug-sweep
Branch: feature/r93-b3-s17-dual-envelope
Audit: docs/audits/r93-b3-s17-dual-envelope-audit.md
Contract: docs/contracts/r93-b3-s17-dual-envelope-contract.md
β task: 1a267787-b864-4f5f-a7dd-669a6268dbe7
§1. Edit list
| # | File | Operation |
|---|---|---|
| 1 | docs/spec/s17-mcp-surface.md |
Edit §6 “Response shape” (lines 77-91 of the existing file). Replace single error-envelope block with §6.1 Success envelope, §6.2 Failure envelopes — two patterns, §6.3 Pattern attribution by tool family. |
No code edits. No other doc edits.
§2. Replacement text (canonical)
## 6. Response shape
The α middleware emits a uniform success envelope. Failure paths use one of two patterns, depending on whether the handler **threw** or **returned a structured error value**. Both patterns are observable on the live MCP wire and are intentional.
### 6.1. Success envelope
```json
{ "ok": true, "data": { /* tool-specific payload */ } }
Every successful handler return is wrapped in this shape by src/server.ts:357-361. The data payload is the unwrapped return value.
6.2. Failure envelopes — two patterns
Pattern A — middleware-emitted (handler threw). The α middleware catches a thrown error at Stage 4 (src/server.ts:362-375) or rejects an invalid input at Stage 2 (src/server.ts:309-336) and emits:
{ "ok": false, "error": { "code": "INVALID_PARAMS|HANDLER_ERROR", "message": "<human>", "details": { /* optional */ } } }
The JSON-RPC structuredContent IS this envelope; isError: true is also set at the transport layer. Error codes:
INVALID_PARAMS— Zod input validation failed.details.issuescarries the Zod issue list.HANDLER_ERROR— the handler threw at runtime.messagecarries the thrown error’s.message.
Pattern B — handler-returned (no throw). Some handlers return a structured error value as their normal return; the middleware wraps it in the success envelope of §6.1, leaving the domain error inside data:
{ "ok": true, "data": { "ok": false, "error": { "code": "<DOMAIN_CODE>", "message": "...", /* optional fields */ } } }
structuredContent.ok === true but structuredContent.data.ok === false. Clients reading ok at the top level alone will miss the error. This pattern is the canonical “domain error vs transport error” distinction and is documented at src/tools/merkle.ts:392-405 (which calls out the same pattern in src/domains/tasks/repository.ts:700-765).
A defensive client should read both structuredContent.ok AND, when it is true, also inspect structuredContent.data?.ok before treating the response as success.
6.3. Pattern attribution by tool family
| Tool family | Pattern | Notes |
|---|---|---|
α System (server_ping, server_health) |
A | No domain errors; only INVALID_PARAMS is reachable |
ε Skill (skill_list) |
A | Throws on DB error |
ζ Decision Trail (thought_record, thought_record_list, audit_verify_chain) |
A | Throws on Zod failure / SQLite UNIQUE violation |
β Task Pipeline (task_create, task_get, task_update, task_list, task_next_actions) |
B | Returns {ok:false, error:{code:"ERR_NOT_FOUND|ERR_WRITEBACK_REQUIRED|ERR_INVALID_TRANSITION|…"}} as data |
η Proof Store (audit_session_start, merkle_finalize, merkle_root) |
B | Returns {ok:false, error:{code:"ERR_SESSION_EXISTS|ERR_SESSION_NOT_FOUND|ERR_ALREADY_FINALIZED|ERR_NO_RECORDS|ERR_NOT_FINALIZED"}} as data |
λ Reputation (reputation_*) |
A | Throws on Zod failure |
θ Consensus (consensus_*, vrf_eval) |
A | Throws on Zod failure or domain error (ROUND_NOT_FOUND, ALREADY_VOTED, INVALID_KEY) — error code is encoded in the message prefix |
δ Router (router_*) |
A | Throws on Zod failure or FallbackChainExhaustedError |
Both patterns are stable for Phase 0+; clients SHOULD support both. Future phases MAY unify on one pattern, but that is a separate scope (docs/architecture/decisions/).
6.4. Stable error codes
The complete catalogue of stable error codes per tool — both Pattern A INVALID_PARAMS|HANDLER_ERROR.message strings and Pattern B data.error.code values — lives in docs/reference/mcp-tools-phase-0.md. That catalogue is the source of truth for the precise string each tool produces.
## §3. Build/lint/test
Doc-only change → no source impact. Run `npm run build && npm run lint && npm test` to confirm baseline behaviour (3492 / 79 suites + documented flakes).
## §4. Commit message template
docs(r93-b3): document dual error-envelope pattern in s17
The α middleware emits one success envelope but two distinct failure
envelopes: Pattern A (handler-threw → middleware wraps as
{ok:false, error}), Pattern B (handler-returned error envelope as data
→ middleware wraps as {ok:true, data:{ok:false, error}}). Both are
intentional and stable on the Phase 0+ MCP surface, but the spec at
s17 §6 documented only one. A naïve client that checks
structuredContent.ok alone misses Pattern B errors.
This patch restructures s17 §6 into three subsections: 6.1 Success envelope 6.2 Failure envelopes — two patterns 6.3 Pattern attribution by tool family
References the canonical Pattern B definition at src/tools/merkle.ts:392-405. No code touched.
Closes R93 B3. ```
Proceeding to implement.