Files
PaperClipAI/packages
DottaandClaude Opus 5.5 683fd1e9a5 test(restart-chaos): cells for staging's no-restart failures (re-wake, Nth recovery step, long turn, K15 without volume) (#20893)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The restart-chaos harness (`tests/restart-chaos/`) tests that agent
work survives control-plane faults
> - In one 8-hour staging window, 20 of 25 failed runs happened with no
restart at all, so the harness did not see them
> - The harness had blind spots: one short task per agent, no wake right
after a successful run, no fault in a later recovery step, short turns,
and a redeploy that always keeps its instance home
> - This pull request adds cells for those shapes, so the harness
reproduces these failure classes on master
> - The benefit is that fixes for these classes can be proven red and
green with the harness, without staging

## Linked Issues or Issue Description

**What happened?**
On staging, an agent ran 10–13 tasks at once on reused sandboxes. 30–60
s after a long run succeeded, an ordinary wake (a comment or an issue
monitor) started the next run of the same task. That run or its recovery
failed before any provider work, with errors such as
`runner_remote_process_identity_unavailable`, `Controller journal drain
timed out` and `journal_tool_input_unanswered`. Other failures happened
in a later recovery step (a held agent-directory lock, a missing runtime
asset after a deploy without a volume).

**Expected behavior**
The chaos harness can reproduce each of these shapes on demand, and
grades them: no failed attempt, every task `done`, and no task left
blocked without a working Retry.

**Steps to reproduce**
See the commands in the README sections "Re-wake after success", "Fail
the Nth recovery step", "Long turn" and "Redeploy without a volume, with
overlap".

Related: #20844 (session fault SR1). W2C is the configuration change
after a successful run; SR1 is the same change while a question is
parked. This PR does not add or change `session-faults.ts`.

## What Changed

- New workloads W2, W2Q, W2C (`rewake.ts`): phase 1 ends with the task
left open; the harness, as the board, wakes the same task the seeded
0–60 s after its run settled. W2 uses a comment or a board-scheduled
issue monitor (by nonce). W2Q parks on an unanswered
`request_human_input` question and is woken by a comment before the
answer. W2C changes the agent's `adapterConfig.idleTimeoutMs` before the
wake. Oracle: phase 1 succeeded, the wake was served by a run that
succeeded, `no_failed_attempt`, `done`, markers.
- `--faults K0`: cells with no fault, for workloads whose shape is the
fault.
- `--fail-recovery-step <step>[:<nth>]` (`recovery-step-faults.ts`):
when the wave's first restart fault starts its replacement, the harness
arms one crash point in a new fail mode; the nth hit throws the error of
the real step, once. Steps: `runnerd-start` (`spawn paperclip-runnerd
EAGAIN`), `teardown` (provider destroy did not finish), `directory-lock`
(`Agent directory operation is already active`). Crossed tasks get
`recovery_step_failed_once` and `no_failed_attempt`.
- Crash points (`packages/shared/src/crash-points.ts`): fail mode in the
control file (`<token> <name>:<nth> fail [pid=<n>]`), with an optional
call-site error; `pid=` binds the line to one server process. The
harness arms the step for the replacement only and disarms it once its
receipt lands. New point
`agent_directory.operation_lock.before_acquire`. The teardown point
moved into its `try`, so fail mode behaves like a failed provider
destroy; kill mode is unchanged. All points stay disarmed under
`NODE_ENV=production`. The runner package's own crash-point copy is not
changed (runner image content ID unchanged).
- `--long-turn-minutes <n>` (`long-turn.ts`, 15–60, opt-in): every
task's first turn runs rounds of real tool work for n minutes before its
workload. Checks `long_turn_rounds` and `long_turn_run_observed`.
- K15 `--k15-fresh-home` (Daytona) and `--k15-overlap-seconds
<min>-<max>` (`redeploy.ts`, `supervisor.ts`, `edge-proxy.ts`): each
deploy generation gets a new, empty instance home and no volume
variables; the next generation boots beside the old one behind an edge
proxy on the wave's port, takes traffic once ready, and the old one gets
SIGTERM after the overlap.
- `tests/runner-e2e/source-sdk-public-inputs.json`: refreshed pins for
the changed shared crash-point files.
- README: one section per new cell, with what it covers.

## Verification

- `pnpm exec vitest run --config tests/restart-chaos/vitest.config.ts`
(48 files, 581 tests after rebasing on master, includes the new
`shape-cells.test.ts`).
- `pnpm exec vitest run packages/shared/src/crash-points.test.ts
server/src/__tests__/crash-point-call-sites.test.ts` (fail mode, call
sites).
- `node scripts/typecheck-test-projects.mjs tests/restart-chaos`.
- Chaos waves on master `1d67e309e0` server code (Daytona image
`ghcr.io/paperclipai/paperclip-daytona-runner@sha256:99fc2671fc320816407f63cffd3acbd27471621febcd26494fe25bb5d7c5dd77`,
content ID `1836c515…`), all RED unless noted:

| Cell | Wave | Result on master | Staging class |
| --- | --- | --- | --- |
| W2/W2Q/W2C, 10 tasks on one Codex agent, `--daytona-warm`, K0 |
`w-20261111T163206-s5201` | 9/10 fail. 3 tasks (W2 t02, W2C t03, t06):
first fault `native_runner_process_exited`, same-run reattach,
`native.remote_runner_recovery_failed` with `stage:
process_verification`, `reason: process_not_running`, `runnerLifecycle:
ready`, `runnerSettled: false`, terminal
`runner_remote_process_identity_unavailable`, "Workspace repair
required". 3 tasks (t04, t08, t10): `native_restart_recovery_blocked`
(`native_retry_source_superseded`) while its successor ran (R4
`concurrent_runs`). 3 W2Q tasks: the answer's continuation was cancelled
`issue_not_in_progress` (status `blocked`), and the task needed Retry. 2
W2C tasks then looped: 60+ succeeded runs in 20 minutes on an
`in_progress` task. | D′/T terminal step reproduced with no restart, no
governed wait and no PRP timeout (same error code and refusal fields).
C+ retry outcome (ownership ambiguous) reproduced as
`native_restart_recovery_blocked`. Not reproduced: `Controller journal
drain timed out`, `did not durably suspend`,
`journal_tool_input_unanswered`. |
| Same, local `--lifecycle warm` | `w-20261111T160103-s5101` | 4/10
fail. 3/3 W2Q: continuation cancelled `issue_not_in_progress`, Retry
needed. 1 W2C: the same `in_progress` run loop (39+ runs). | The
remote-runner classes need a remote runner; locally they do not show. |
| `--fail-recovery-step runnerd-start:1`, K1 at 10 s, local A1 |
`w-20261111T162432-s5303` | Fail. After K1 the first runnerd start of
recovery failed (`spawn paperclip-runnerd EAGAIN`); the run ended
`native_provider_terminal_failed` ("previous turn is no longer active",
"Automatic recovery stopped") and needed Retry. Control, same seed and
K1 without the step fault (`w-20261111T171114-s5303`): pass. | A later
recovery step that fails once ends the run instead of trying again (the
A-chain shape). |
| `runnerd-start:2`, local | `w-20261111T170909-s5304` | Step never
reached: recovery started the runner once and succeeded. Grade fails
`recovery_step_failed_once` (tested nothing). | — |
| `teardown:1`, K1, Daytona A1 | `w-20261111T172048-s5312` | Pass. The
teardown failed once and the sweep settled it. | Not reproduced. |
| `directory-lock:1`, K2, Daytona A1 | `w-20261111T171247-s5311` | Step
never reached by A1 after K2. | Not reproduced; A1 recovery does not
collect remote agent files. |
| K15 `--k15-fresh-home --k15-overlap-seconds 30-150`, `--storage s3`,
Codex and OpenCode, T1/C1 | `w-20261111T173618-s5322` | 1/4 fail
(OpenCode T1): `native_continuation_requires_reconciliation` with "Agent
directory belongs to a different execution environment"; the Retry runs
failed `adapter_failed` "Configured instruction entry is missing"; the
task rests `blocked` on a board recovery action, and two sandboxes were
left (R6). One Codex T1 task finished before its fault
(`fault_crossed_tool`). | A-deploy: a controller-local file missing
after a deploy without a volume. Here the missing file is the agent's
instruction bundle, not a runtime-context asset; whether staging keeps
instruction bundles outside the instance home is not verified. |
| `--long-turn-minutes 15`, K1 at 600 s, local A1 |
`w-20261111T175505-s5401` | Task pass (10/10 rounds, 992 s span, run
1019 s, K1 crossed mid-turn). The wave exited 2 only because I switched
branches in the worktree during the wave. | Shape check only. |


## Risks

- Product code changes are test instrumentation only: three `crashPoint`
call sites take a fail-mode error, and one new call site was added.
Disarmed (always in production) each call is one comparison. Moving the
teardown point into its `try` does not change kill mode: the process
dies at the same instant.
- The new cells are opt-in. Default waves plan the same tasks as before
for the same seed.
- The W2 cells depend on model behaviour (phase 1 leaves the task open).
The oracle records the task status at the wake.
- `--k15-fresh-home` drops everything under the instance home. Whether
staging keeps agent instruction bundles outside the instance home is not
verified (see the K15 result).

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, agentic coding with
tool use (shell, file edits, test and chaos runs) in Claude Code.

## 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

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 12:58:41 -06:00
..