Repository navigation
Commit 9633fff
fix(service-automation): RESUME_CHILD_PENDING's remedy names every run of the suspension chain (#28516)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XD327LjMjJZx2oCbKfZMtNc)_
Fixes #28202
Clause-②: no (message wording only)
Metadata-protocol: none
## 维护者速读
- **改了什么**:批处理(`map`)运行的子项运行已消失、又没有 `cancelled` 历史时,恢复会被
`RESUME_CHILD_PENDING`(409)拒绝;拒绝文字里「要结束这批,请取消哪些运行」现在列出整条挂起链上的每一个运行,从 map
自己的运行起,依次往上,直到被恢复的那个运行。
- **为什么改**:卡 #28202(分诊 6528511645,来自 PR #28176 合同评审
S3/S4):取消一个运行不会连带取消等它的运行。map 在被恢复运行之下隔了不止一层 `subflow:`
时,旧文字只点名两头,运维照做后中间那层仍然挂着。
- **风险与回滚**:只改文字。错误码、状态码和合同文字都不变;两层链的文字与改前逐字相同。回滚即 revert。
- **先看**:`packages/services/service-automation/src/run-resume.ts`(新函数
`waitingRunsClause` 和它在 `continuePastCancelledMapItem`
里的唯一调用处);`packages/services/service-automation/src/map-cancelled-item-resume.test.ts`(新增两层、三层两条用例)。
- **两分钟验证**:`pnpm --filter @objectstack/service-automation exec vitest
run --maxWorkers=2 src/map-cancelled-item-resume.test.ts` → `Tests 9
passed (9)`。
- **需要你做的**:无,席位落地。
## What changes
`continuePastCancelledMapItem` ended its remedy with "To end the batch,
cancel run 'MAP'", plus " and run 'RESUMED', which waits on it" when
nested. It named `link.runId` and the resumed `runId` only. The `chain`
it already receives (from `resolveSuspensionChain`, bounded by
`MAX_SUSPENSION_CHAIN_DEPTH` = 32) holds every link, so the new helper
`waitingRunsClause(chain)` names every run above the map, nearest first:
| Chain | Remedy tail |
| :--- | :--- |
| MAP resumed directly | `cancel run 'MAP'` (unchanged) |
| OUTER → MAP | `cancel run 'MAP' and run 'OUTER', which waits on it`
(unchanged, byte for byte) |
| OUTER → MID → MAP | `cancel run 'MAP' and runs 'MID' and 'OUTER',
which wait on it in turn` |
| deeper | `cancel run 'MAP' and runs 'A', 'B' and 'OUTER', which wait
on it in turn` |
The list needs no cap of its own: the chain walk's depth bound already
bounds it. The code (`RESUME_CHILD_PENDING`), the 409 status, the log
line and every other sentence of the message are unchanged.
`refuseMapItemSkip` (the still-suspended item) names one run to resume,
not runs to cancel, so it is untouched.
## Tests
Two new cases in `map-cancelled-item-resume.test.ts`, inside the
existing `#27971` describe and its fixtures:
- **two-level** (OUTER → MAP, item row gone, history `paused`): pins the
nested suffix `To end the batch, cancel run 'MAP' and run 'OUTER', which
waits on it; `, plus code and nothing moved. Card S4 noted this suffix
was not pinned before.
- **three-level** (OUTER → MID → MAP): pins `... cancel run 'MAP' and
runs 'MID' and 'OUTER', which wait on it in turn; `, code, and that the
map state and all three suspensions are unchanged.
The two direct pins (`To end the batch, cancel run '${paused.runId}'`,
≈L183/L200) are untouched and green.
<details><summary>Red first on origin/main 1f1a369 (test only, no
fix)</summary>
```
× names every run of a three-level chain in the remedy, from the map up to the resumed run
Received: "... Nothing was changed. To end the batch, cancel run 'run_6e14…' and run 'run_2655…', which waits on it; if the run history store was unreachable, retry once it is readable"
Tests 1 failed | 8 passed (9)
```
The base names MAP and OUTER only; MID is missing. The two-level case is
green on the base (its text is unchanged).
</details>
<details><summary>Green and package suite</summary>
- `vitest run src/map-cancelled-item-resume.test.ts` → `Tests 9 passed
(9)`, exit 0.
- Under the verify lock: `pnpm --filter @objectstack/service-automation
build && … typecheck && … test` → `Test Files 249 passed (249)`, `Tests
3164 passed (3164)`, `VERDICT command-exit 0`.
- `eslint packages/services/service-automation --no-inline-config` →
exit 0.
</details>
<details><summary>Ablation (on the fix, committed before the
mutation)</summary>
Restored the two-name form through `node scripts/ablation-replace.mjs
--hold` on `run-resume.ts` (injected-text count 1, removed-text count 0
on disk). The test imports `./run-resume.js` from `src/` relatively, so
no `dist/` build sits on the path. Result: `× names every run of a
three-level chain …`, `Tests 1 failed | 8 passed (9)`: the three-level
case only. Restored with `--restore` + `git checkout HEAD -- PATH`; `git
diff HEAD` empty and the file's hash equals the `HEAD` blob
(`3db6ab0c`).
</details>
## Acceptance notes
- The message's lead still says "Run 'OUTER' is waiting on run 'MAP'" in
a three-level chain, which is true transitively. The card scoped the
change to the remedy, so the lead is left as is.
## Verification
All read on head `8bb5561b` (same tree `0b745620` as the commit the gate
loop ran on before the trailer reword).
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 61 derived families. Each run with its exit
code captured before any pipe; `--ran` reconciliation: `✓ 61 derived
famil(ies) accounted for — 60 run, 1 NOT-MEASURED`.
- 60 exit 0. `node scripts/check-plugin-teardown-shape.mjs --self-test`
first exited 3 on the shallow clone (positive-control commit
unreachable); after `git fetch --depth=1 origin 621a487…` it exits 0
(`48 cases pass`).
- NOT MEASURED: `pnpm check:dual-build-cjs-loads`, reason: PREREQUISITE
NOT MET (39 packages outside this diff's build closure have no `dist/`);
CI builds the workspace and runs it. The diff changes no export or
entry.
- `test:repo` not run locally (the diff touches no repo-project test);
CI Test Core runs it.
- Families CI runs beyond the derived list: read from this PR's
check-runs.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5m4fAx5zTbY8tmNPQm)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 35b178b commit 9633fff
3 files changed
Lines changed: 80 additions & 1 deletion
File tree
- .changeset
- packages/services/service-automation/src
| 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 | |
|---|---|---|---|
| |||
201 | 201 | | |
202 | 202 | | |
203 | 203 | | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
204 | 259 | | |
205 | 260 | | |
206 | 261 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
586 | 586 | | |
587 | 587 | | |
588 | 588 | | |
589 | | - | |
| 589 | + | |
590 | 590 | | |
591 | 591 | | |
592 | 592 | | |
| |||
600 | 600 | | |
601 | 601 | | |
602 | 602 | | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
603 | 617 | | |
604 | 618 | | |
605 | 619 | | |
| |||
0 commit comments