Skip to content

Commit fce8981

Browse files
committed
docs(adr): ADR-0104 D2 note states what a D2 refusal is on main
The dated note under D2 item 1 now says a D2 refusal is one 400 VALIDATION_ERROR naming every offending param, with no fields[], at the REST and MCP doors, and that item 1's "400 VALIDATION_FAILED per-field envelope" names the ledger's record-level fields[] member instead. The status-line entry carries the same correction. The original sentence stays verbatim. Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5823853 commit fce8981

1 file changed

Lines changed: 24 additions & 5 deletions

File tree

‎docs/adr/0104-field-runtime-value-shape-contract.md‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
declarative `operation: 'update'` action's empty or absent `params` list is
1919
an empty accept set, refused through D2's own path, and at the MCP
2020
`run_action` door a D2 refusal carries `code` + `status` (the ADR-0112
21-
envelope), as at the REST door. See the note under D2 item 1.
21+
envelope), as at the REST door. A D2 refusal is one `400 VALIDATION_ERROR`
22+
naming every offending param, with no `fields[]`, at both doors, which
23+
corrects item 1's "`400 VALIDATION_FAILED` per-field envelope". See the
24+
note under D2 item 1.
2225
- **Date**: 2026-07-22
2326
- **Issue**: design follow-up generalizing #3405 / #3406 (inline lookup param
2427
silently stripped); relates #3407 (silently dropped writes), #1878 / #1891
@@ -215,8 +218,21 @@ free. Three changes:
215218
> (`packages/spec/src/ui/action-params.zod.ts#ACTION_PARAM_BUILTIN_KEYS`:
216219
> `recordId`, `objectName`, `_selectedIds`) is refused through this item's
217220
> path: the same 400 at the REST door and the same
218-
> `OS_ALLOW_LAX_ACTION_PARAMS` escape hatch, with no switch of its own. An
219-
> empty bag still writes the `patch`. The discriminator is `operation`
221+
> `OS_ALLOW_LAX_ACTION_PARAMS` escape hatch, with no switch of its own.
222+
> **What a D2 refusal is, at both doors and for every action:** one
223+
> `400 VALIDATION_ERROR` whose message (`Invalid action params: …`) joins
224+
> one issue per offending param, each naming it, with no `fields[]`. The
225+
> REST door passes no code, so
226+
> `packages/runtime/src/error-envelope.ts#buildApiError` derives it from
227+
> the status through
228+
> `packages/spec/src/api/errors.zod.ts#standardErrorCodeForHttpStatus`;
229+
> the MCP throw below stamps that function's answer. So item 1's "the
230+
> standard `400 VALIDATION_FAILED` per-field envelope" names the wrong
231+
> code and the wrong shape: `VALIDATION_FAILED` is the ADR-0112 ledger's
232+
> record-level validation member, which carries `fields[]`
233+
> (`packages/types/src/validation-failure.ts`), and the D2 gate never
234+
> builds it. An empty bag still writes the `patch`. The discriminator is
235+
> `operation`
220236
> alone (`packages/runtime/src/action-execution.ts#isDeclarativeUpdateAction`),
221237
> so every other param-less action (a script handler, an inline `body`, a
222238
> `flow` target) keeps the pass-through. At the MCP `run_action` door,
@@ -227,9 +243,12 @@ free. Three changes:
227243
> (`packages/mcp/src/mcp-http-tools.ts#thrownRefusalEnvelope`), as at the
228244
> REST door. Pinned in `packages/runtime/src/action-params-enforcement.test.ts`
229245
> (the gate, the hatch), `packages/runtime/src/action-declarative-update.test.ts`
230-
> (both doors, nothing written, and the handler control) and
246+
> (both doors: `VALIDATION_ERROR` and 400 on a declared and a param-less
247+
> action at REST and on the MCP throw, nothing written, and the handler
248+
> control) and
231249
> `packages/mcp/src/mcp-coded-refusal.test.ts` (`run_action`'s rendering of
232-
> a thrown code). No other part of D2 changes.
250+
> a thrown code). No other decision in D2 changes: the code and shape
251+
> above correct the record, they add no rule.
233252
234253
2. **Typed handler surface.** `defineAction` today validates config only. It
235254
gains a typed companion so registered handlers stop being

0 commit comments

Comments
 (0)