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
fix(actions)!: failures speak HTTP — rejections are 400, success is a single wrap (#3962) (#3969)
Fixes#3962 — the platform decision classifying the 200-on-failure wire as a
bug, not a contract. The 200-with-inner-envelope shape was never designed: no
ADR or doc specified it, it originated as the catch block reusing
deps.success(), and /actions was the only route of 12 that double-wrapped.
Five defects traced back to that one extra layer.
Contract now, identical to /data:
- ran, returned → 200 {success:true, data: <handler return value>} (single wrap)
- ran, rejected → 400, semantic code on error.code (VALIDATION_FAILED with
fields[] in details; FLOW_FAILED for a rejected flow)
- never dispatched → 404 / 403 / 400 / 503 (unchanged, #3930/#3951)
- crashed → 500 (unchanged, #3951; name-based discriminator now
selects 400 vs 500)
actions-validation-envelope.test.ts pinned the 200 "so flipping it later is a
conscious, documented break"; #3962 is that decision and the flipped test cites
it. Integrates ADR-0110 (#3958/#3987 — name identity, undeclared refusal with
no opt-out) and #3971 (semantic error.code, details.code promotion) from main.
client.actions.invoke/invokeGlobal still never throw: every failure status
folds into {success:false, error}, success reads the single wrap, and a narrow
legacy heuristic (boolean success, no foreign keys) keeps a current SDK correct
against pre-#3962 servers.
Migration (raw-HTTP callers): branch on the status; on 200, data is the
handler's return value directly. SDK callers need no change.
0 commit comments