Repository navigation
Commit 8e6ab05
docs(spec): document typed approval decision outputs as enforced at the decision, not a widget hint (#29371)
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9SVUDueboMTukTWyoTJh412)_
Fixes #29340
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:审批节点「决策输出」的 spec 注释(三处)和审批文档一段,改成「`type`/`multiple`
在决策时强制校验」,删掉「只影响输入控件」的旧说法。
- 为什么改:#28658 裁决 A 要求运行时按声明校验决策输出;旧文字与 ADR-0104 2026-10-30 修订矛盾,且服务端
PR(席 5)要等这条文字先落地。
- 风险与回滚:只改注释和文档,无 schema、键、describe、导出变化;回滚即 revert 本 PR。
-
先看:`packages/spec/src/automation/approval.zod.ts`(DecisionOutputDefSchema
注释块)、`content/docs/automation/approvals.mdx`(typed declaration 段落)。
- 两分钟验证:`git grep -n "only shape"
packages/spec/src/automation/approval.zod.ts
content/docs/automation/approvals.mdx` 应无输出。
- 需要你做的:无,席位落地。
## What changes
- `packages/spec/src/automation/approval.zod.ts`, three docblocks (no
code, no `.describe()` string):
- `DecisionOutputDefSchema`: replaces "`type` and `multiple` only shape
the INPUT WIDGET, never the accepted value" with the enforced contract:
a picker output is judged by record id (`user`, `department`, `team`) or
by machine name (`position`), through the lookup the matching
`expression` approver's `resolveAs` uses; `multiple: true` takes a list
with no entry twice, absent/false takes one value; a mismatch is refused
with `400 VALIDATION_FAILED` naming `outputs.KEY` before any write, the
request staying pending; `multiple` binds every entry, text and bare
keys included; text is judged by shape only.
- `required`: "Unlike `type`/`multiple` — which only shape the input
widget" becomes "`type`/`multiple` judge the SHAPE of a supplied value;
this one judges PRESENCE".
- `decisionOutputs` on the approval node config (a third copy the card
did not name): "The type describes the INPUT WIDGET only — the runtime
accepts whatever value shape arrives … verbatim" is rewritten to the
same contract.
- `content/docs/automation/approvals.mdx`: the typed-declaration
paragraph drops "The type shapes only the input widget" and gains the
enforced rule (a short list), and the `required` paragraph no longer
contrasts against an unenforced `type`/`multiple`.
- `content/docs/automation/approvals.mdx` `next_reviewers` example now
declares `{ key: 'next_reviewers', multiple: true }` (the bare key fed a
list, which #28658's server PR refuses).
- `.changeset/29340-decision-output-enforced-docs.md`:
`@objectstack/spec` patch (the docblocks ship in the published
`src/**/*.zod.ts`).
No `.describe()` string carried the contradiction (checked: the `type`,
`multiple` and `decisionOutputs` describes only say what the approver
answers with), so nothing regenerates. Forced test edits: none.
Ordering: the runtime enforcement is #28658's `domain:server` PR (branch
`claude/project-thread-rsqu0h`), which merges after this one; the text
is written as the contract, in the present tense, as ADR-0104's
amendment is.
## Acceptance notes
- **Error code: `VALIDATION_FAILED`, not the card's
`VALIDATION_ERROR`.** The approvals door answers `400 VALIDATION_FAILED`
for a malformed decision (the guide's own error-code table; the existing
`required` refusal throws `VALIDATION_FAILED:`), and the enforcement
branch for #28658 (head `88c96ad6`) throws `VALIDATION_FAILED` naming
each `outputs.KEY`. `VALIDATION_ERROR` is the action-params gate's code
from ADR-0104 D2. The text names the code the door answers.
- **Approve and reject alike.** The guide says the typed check runs on
an approve and a reject alike, matching the enforcement branch ("each
admitted output is judged … on an approve and on a reject alike"). If
that PR changes its reject choice, this sentence moves with it.
- `plugin-approvals/src/approval-service.ts` carries the same "only
shape the input widget" comment beside the `required` check; that file
is the server half's, so it is left to that PR.
## Verification
Run on head `fc024e80` (round 2: two text-only commits over `821642ea`).
- `pnpm --filter @objectstack/spec build && pnpm --filter
@objectstack/spec check:generated`: `✓ All 14 generated artifacts are up
to date`, lock VERDICT command-exit 0.
- Offline changeset check of this body: `node
scripts/check-changeset-no-major.mjs --base origin/main --event FILE`
exit 0, `✓ LEVEL AXIS` (clause-② no, metadata-protocol none: a
non-protocol change at patch). The card's `Clause-②: no (narrowing)` was
also measured: the gate accepts it too (exit 0), but reads the arm as
BREAKING, which a text-only diff is not, so this body declares bare `no`
as the seat's claim does.
- Derived gate list (`dispatch-gates --commands`, 96 commands): 96/96
exit 0 after a full `turbo run build --filter=./packages/**` (lock
VERDICT command-exit 0); `dispatch-gates --ran`: `96 derived famil(ies)
accounted for — 96 run, 0 NOT-MEASURED`.
- `pnpm --filter @objectstack/spec typecheck && pnpm --filter
@objectstack/spec test`: `Test Files 791 passed (791)`, `Tests 23391
passed | 1 todo`, lock VERDICT command-exit 0.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind. (No name
added.)
- [x] 3. No formatter ran; `pnpm lint` passes (CI).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed (none stale).
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
maintainer's Chinese summary).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmP8ELNFNfBHmk387wA)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3291213 commit 8e6ab05
3 files changed
Lines changed: 61 additions & 14 deletions
File tree
- .changeset
- content/docs/automation
- packages/spec/src/automation
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
327 | 327 | | |
328 | 328 | | |
329 | 329 | | |
330 | | - | |
331 | | - | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
332 | 334 | | |
333 | 335 | | |
334 | 336 | | |
| |||
339 | 341 | | |
340 | 342 | | |
341 | 343 | | |
342 | | - | |
343 | | - | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
344 | 364 | | |
345 | 365 | | |
346 | 366 | | |
347 | | - | |
| 367 | + | |
| 368 | + | |
348 | 369 | | |
349 | 370 | | |
350 | 371 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
656 | 656 | | |
657 | 657 | | |
658 | 658 | | |
659 | | - | |
660 | | - | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
661 | 672 | | |
662 | 673 | | |
663 | 674 | | |
| |||
696 | 707 | | |
697 | 708 | | |
698 | 709 | | |
699 | | - | |
700 | | - | |
| 710 | + | |
| 711 | + | |
701 | 712 | | |
702 | 713 | | |
703 | 714 | | |
| |||
1201 | 1212 | | |
1202 | 1213 | | |
1203 | 1214 | | |
1204 | | - | |
1205 | | - | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
1206 | 1218 | | |
1207 | 1219 | | |
1208 | 1220 | | |
1209 | 1221 | | |
1210 | 1222 | | |
1211 | | - | |
1212 | | - | |
1213 | | - | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
1214 | 1230 | | |
1215 | 1231 | | |
1216 | 1232 | | |
| |||
0 commit comments