Files
PaperClipAI/tests
DottaandClaude 1a89fca1ce test(restart-chaos): add --rr-stop-on recovering so RR follow-up stops land inside recovery (#20511)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The restart chaos harness (`tests/restart-chaos/`) tests if agent
tasks survive control-plane restarts.
> - #20480 added repeated restarts (RR1–RR3): a K1, then more restarts
while the replacement recovers.
> - In the first four live waves, no follow-up stop landed inside
recovery. Every stop saw `/api/ready` report `ready`.
> - The saved receipts show why. `/api/ready` reports `recovering` only
15–80 ms before `/api/health` answers, and `ready` 0.25–0.5 s after it.
A gap measured from health, even 1 s, always lands after recovery.
> - This pull request adds `--rr-stop-on recovering`. Each follow-up
stop fires on the first `recovering` status, polled from the
replacement's spawn.
> - The benefit is that RR waves now test a restart that interrupts
recovery itself.

## Linked Issues or Issue Description

Refs #20480

**What is the problem?**
RR follow-up stops wait a gap from the replacement's health. On a local
host recovery ends about 0.3 s after health, so no stop in four waves
(14 tasks, 18 follow-up stops) landed inside recovery.

**What is the solution?**
Add `--rr-stop-on recovering`. It replaces the gap: the harness polls
`/api/ready` every 50 ms from spawn and stops the replacement on its
first `recovering` status. This is the phase poll that K6 already uses.

**Why is it needed?**
The deploy loops of 2026-11-08 and 2026-11-09 restarted the server while
it recovered interrupted runs. The harness must reach that window to
test it.

## What Changed

- `repeated-restarts.ts`: `--rr-stop-on recovering` is parsed by
`parseRepeatedRestartOptions`. It needs an RR fault and does not combine
with `--rr-gap-seconds` or `--replay`. `runRepeatedRestarts` takes
`stopOn`: with it, no gap is slept, each stop waits on the new
`waitForStopPhase` hook, and only the last replacement is waited on to
health.
- `supervisor.ts`: `waitForStopPhase` reuses the K6 phase poll
(`waitForCrashLoopPhase`, 50 ms). The record carries `stopOn`.
- `types.ts`, `config.ts`, `main.ts`: the `rrStopOn` option, its flag,
and one line to pass it to the supervisor.
- The evidence shows each stop's `/api/ready` status (`phaseAtStop`).
With `stopOn`, `gapMs` is null, and the earlier entries have no
`healthyAt`.
- README: the option and the measured timings.

## Verification

- `pnpm exec vitest run --config tests/restart-chaos/vitest.config.ts`:
35 files, 438 tests pass. New tests cover option parsing, stops on the
first `recovering` with no sleep, health waits only on the last
replacement, a `ready` stop when recovery was too short to see, an exit
before a status, and a missing watcher.
- `node scripts/typecheck-test-projects.mjs tests/restart-chaos` passes.
- A live wave from this branch (`--workloads A1,B5,C1 --faults RR1,RR3
--rr-stop-on recovering`, codex, 4 tasks) follows; its results go to a
comment.

## Risks

- Harness-only change; no product code changes. RR waves without the
flag are unchanged.
- A stop on `recovering` lands before the replacement answers health.
This is the K6 behavior, and the server log shows it. A replacement that
never reports a status fails the fault after the 5-minute ready timeout,
as K6 does.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Anthropic Claude, model ID `claude-opus-5-5`, used through Claude Code
with tool use (shell, file edits) and extended thinking.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-10 03:56:25 -06:00
..