Repository navigation
Commit 5a84d41
* wip: rescue in-progress work before container restart loss (flows)
* fix(automation): validate screen resume input against the node's declared fields (#4477)
Changeset for the screen-resume field contract landed in the preceding commit
(which the container restart forced out as a WIP rescue): `resume` now refuses a
bag that violates the suspended screen's declared `fields` — a missing `required`
value the caller WAS asked for, or a key the screen never declared — with the new
`INVALID_SCREEN_INPUT` code, mapped to 400 by the automation domain route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEHJN2NFpS9VMeURvakgD
* fix(approvals): record an admin override of a staffed approver slate as an override (#4466)
`sys_approval_action` had no override column, so an admin overriding a properly
staffed approver slate wrote a row byte-for-byte identical to the designated
approver approving normally. A reader of the timeline saw `approve` by the admin
and could not tell which had happened; the bypassed approver's `409
INVALID_STATE` was the only trace, and only if they happened to try. The platform
knows at decision time — it took the `isOverrideActor` branch to admit the call —
so this was dropped information, not unavailable information.
Adds `sys_approval_action.via_override` (optional boolean), set on exactly the
actions admitted by that branch: `decideNode`'s approve/reject and `reassign`'s
admin rescue. Surfaced on `ApprovalActionRow`, returned by `listActions`, and
added to `highlightFields` + two grid views.
`true` = admitted only by the override branch. `false` = checked, not an override
(an admin who IS a slot holder approves normally). Absent = a row predating the
column — "not recorded" is not the claim "not an override", so `rowFromAction`
maps `null` to `undefined`. Additive and nullable: no migration needed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEHJN2NFpS9VMeURvakgD
* fix(automation): type screen-input issue codes as FieldErrorCode, exempt the file from ADR-0112 D1
`check:error-code-casing` flagged `ScreenInputIssue.code`'s `'required' |
'unknown_field'` literal union as a lowercase `error.code`. It is not one: these
are FIELD-ADDRESSED validator codes (ADR-0114 D2, ADR-0112 D6) carried inside the
refusal's message, while the refusal's own machine code is the SCREAMING
`INVALID_SCREEN_INPUT` the engine returns.
Typed as the shared `FieldErrorCode` for the reason `ActionParamIssue` gives — a
screen field, an action param and a record column must not drift into three
vocabularies for the same two conditions — and registered in the checker's
EXEMPT_FILES with its reason, beside the `action-params.zod.ts` precedent it
mirrors exactly.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEHJN2NFpS9VMeURvakgD
* fix(approvals,verify): find stranded terminal requests; stop the harness pinning memory (#4469, #4470)
#4469 — `releaseDeadRunRequests` scans `status: 'pending'`, and the very step
that zombifies a request is the one that takes it OUT of `pending`: breaking it
removed it from the only sweeper's field of view. Its oracle could not have
answered anyway — `getRun` reads the execution LOG, which returns null for a
perfectly alive suspended run after a restart.
Adds `ApprovalService.inspectStrandedRequests()`, a READ-ONLY inspection over
`approved`/`rejected`/`returned` rows that uses BOTH oracles and reports only
rows failing both: `hasSuspendedRun(runId) === false` (no live pause) AND
`getRun(runId) == null` (no terminal history row either). A store that THROWS is
skipped and counted as `undetermined`, never condemned — an outage means unknown.
It never rewrites a decision that really happened; it reports which requests are
stuck at which step and what the mirrored status field still reads, and rides the
existing sweep clock so the finding surfaces without an operator going looking.
#4470 — `packages/verify/src/harness.ts` pinned `suspendedRunStore: 'memory'`,
making the DB-backed path structurally unreachable from every dogfood/e2e
fixture. Engine-side persistence was unit-tested against a fake table and the
approval chain e2e-tested wholly in memory, while the assembly between them was
covered by nothing — the seam #4420 grew in. The harness now boots the plugin's
own `'auto'` default (a fixture opts out explicitly), and gains `databaseFile` so
two sequential boots over one file are a genuine cold start.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEHJN2NFpS9VMeURvakgD
* wip: rescue in-progress work before container restart loss
* test(dogfood): assert what the durable suspended-run row actually contains (#4470)
CI showed the harness store change (`'memory'` → the plugin's own `'auto'`)
regressed nothing: 38 dogfood files and 239 tests passed, and the single failure
was this file's own cold-boot assertion.
Investigating that failure found a real blocker, so the proof is rewritten around
what can be established rather than around what would be convenient. A second
`bootStack` over the same `databaseFile` reads a database whose tables exist but
whose ROWS are gone — ordinary records do not survive it either, which is what
identifies it as a harness/driver persistence gap rather than a defect in the
suspended-run store. #4470's third bullet is therefore documented as a KNOWN GAP
in the file header, not faked with a weakened assertion.
What is asserted instead is the substance of the issue's first two bullets, and
all of it is a fact rather than the absence of an error:
- `sys_automation_run` exists and is queryable (the table #4420 was missing);
- the pause lands as a real `paused` row, read back by id, carrying every
field a rehydration needs — `node_id`, the `node_type` the resume gate keys
on, a parseable `variables_json` holding the trigger input, `steps_json`,
and the `screen_json` a resumed pause validates against;
- resuming CONSUMES that row and leaves the `run_`-prefixed terminal history
row in its place — the zombie shape #4420 produced;
- the screen contract (#4477) is enforced against the persisted `screen_json`,
and a refusal leaves the durable row intact.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017gEHJN2NFpS9VMeURvakgD
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2fa286c commit 5a84d41
22 files changed
Lines changed: 1698 additions & 10 deletions
File tree
- .changeset
- packages
- plugins/plugin-approvals/src
- translations
- qa/dogfood/test
- fixtures
- runtime/src/domains
- services/service-automation/src
- builtin
- spec/src/contracts
- verify/src
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
0 commit comments