mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-13 07:12:00 +01:00
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The restart-chaos harness (`tests/restart-chaos`) restarts the
server during real agent runs and grades the recovery.
> - Several native recovery fixes only apply when a restart lands after
the run has started its native runner. Examples: a restart claim whose
lease lapses in a storage outage (#20529), and a K2 seconds after native
start on a remote sandbox.
> - On Daytona R1, workspace preparation takes 100-250 s and varies a
lot under storage faults. A time-based fault and `--storage-trigger
run-log` both land while the run is still preparing. In three waves,
none reached native start.
> - This pull request adds `--native-trigger`, which fires the planned
fault a set delay after the run's own `native.process_start_requested`
event or first provider event.
> - The benefit is that a wave can reliably restart the server at native
start and prove those recovery paths live.
## Linked Issues or Issue Description
Refs #20529, Refs #20514
**What happened?**
Waves `w-20261110T113240-s11936`, `w-20261110T120901-s11937` and
`w-20261110T124555-s11936` (Daytona R1, K2) were meant to restart the
server after native start. Every K2 landed during remote workspace
preparation, before native runtime selection. The drain took the legacy
interrupt path, so the native recovery path under test never ran.
**Expected behavior**
A wave can fire its planned fault a known number of seconds after the
run starts its native runner.
**Steps to reproduce**
1. Run a Daytona R1 wave with `--faults K2 --fault-after-seconds 110`,
optionally with `--storage-faults ST1`.
2. Read `server.log`. The graceful drain lists the runs under
`interruptedRunIds`, not `restartSuspendedRunIds`.
**Paperclip version or commit**
`master` at adb0e77a4c.
## What Changed
- New `tests/restart-chaos/native-trigger.ts`: `--native-trigger
native-start|provider-event[+<seconds>]` parsing and a `NativeTrigger`.
The trigger arms each task's planned fault at its planned time. Each
wave loop pass, it reads the task's running runs' events through the
board API with a per-run cursor. It fires the fault the given delay
after the first matching event created after arming. It records
receipts, misses (`task_rested`, `no_matching_event`), the fault id, a
summary section and a shortfall check.
- `config.ts`, `types.ts`, `main.ts`: registry lines only. They add the
option, construct the trigger next to `--storage-trigger`, arm faults
instead of firing them by time, tick the trigger in the wave loop, keep
the loop alive while it fires, and write `native-trigger.json` plus the
summary section.
- New `tests/restart-chaos/native-trigger.test.ts`: parsing and
conflicts, event matching, firing after the delay, ignoring events from
before arming and from non-running runs, waiting while another fault
runs, misses, and fault linking.
- `tests/restart-chaos/README.md`: a "Restart at native start" section.
## Verification
- `PAPERCLIP_IN_WORKTREE=false npx vitest run --config
tests/restart-chaos/vitest.config.ts
tests/restart-chaos/native-trigger.test.ts
tests/restart-chaos/storage-trigger.test.ts`: 16 passed. The new tests
fail on `master`, where the option and module do not exist.
- `node scripts/typecheck-test-projects.mjs tests/restart-chaos`:
passed.
- Test-only harness change; no product code changes. A live wave with
this trigger follows as the gate wave for #20529.
## Risks
- Low risk. This is test harness only. A wave without `--native-trigger`
takes no new code path except a no-op `tick` call.
- The trigger polls the board API once per wave loop pass (1 s) for at
most the armed tasks' running runs. That adds a little load on the
server under test.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use and code
execution 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
- [ ] 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 Opus 5.5 <noreply@anthropic.com>