Files
PaperClipAI/tests
DottaandClaude 2c2186706e test(restart-chaos): human-in-the-loop journeys (Q2, Q3), journey triggers and a journey x fault matrix (#20448)
## 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
control plane while real agents work, and grades each task.
> - Almost every harness workload is "the agent does a file task and
finishes". Few workloads have a person in the loop.
> - On 2026-11-09, production failures came from journeys with a person
in the loop: a saved answer that could not reach a dependency-blocked
task (#20363), and an app-action approval that a routine OAuth refresh
or a one-hour deadline made invalid (#20340).
> - No workload exercised these journeys, so no wave could catch them.
> - This pull request adds journey workloads with deterministic oracles
and journey-specific fault triggers. It also adds a journey × fault
matrix and a way to run a wave against an older build.
> - The benefit: one command crosses every journey with every fault
kind, and the same wave proves red before a fix and green after it.

## Linked Issues or Issue Description

Refs #20363, Refs #20340, Refs #20336, Refs #20432

## What Changed

- New workload **Q2** (`journeys.ts`, `journey-driver.ts`): the agent
saves a question with its native `request_human_input` tool. The board
adds an unfinished `backlog` prerequisite, marks the task `blocked` by
it, and answers through the task-thread respond route. The oracle
requires that the answer reaches the agent in bounded answer mode while
the task stays blocked, that the prerequisite relation stays, that no
run has `issue_dependencies_blocked`, and that no stale Retry notice
stays at rest. Then the board finishes the prerequisite and the agent
finishes the task.
- New workload **Q3**: a harness-owned OAuth 2.1 + MCP app on loopback
(`journey-oauth-app.ts`, discovery, dynamic client registration, PKCE,
rotating refresh tokens, one write tool). The board connects it through
the Apps routes and sets the tool to Ask first for a scripted `process`
agent. When the review is pending, the harness moves the review's saved
timestamps and the token expiry back 61 minutes in the wave's own
database, runs Check connection (which refreshes the token through the
app), and approves. The oracle requires one review, a refresh between
request and decision, an accepted approval, exactly one `tools/call`
with the reviewed arguments, an `executed` request, and an agent report
that carries a receipt only the provider result contains.
- New triggers `asked` and `answered` fire a restart fault 0–3 s after
the saved request is first seen and after the board's response. They
work for the existing **Q1** saved question (#20432) too. The board
answers before an `answered` fault fires and after every other planned
fault.
- New `--matrix` option: one task per workload × adapter × (a `K0`
no-fault baseline, each restart fault at each of the workload's own
triggers, each provider message fault). `K0` is a new task fault kind
with no fault.
- New `--server-from <checkout>` option: every server start and restart
of the wave runs another prepared checkout, for example the parent of a
fix, with the same harness code.
- Q3 setup allows unpriced usage on the wave's company budget policy,
because a scripted process run records usage with no price and the
default policy pauses the whole company.
- README documents the journeys, the triggers, the matrix and the
red-before-fix recipe. Unit tests cover the oracles (pass and the
pre-fix failure signatures), the driver's ordering, the matrix plan,
option validation and the OAuth app.

## Verification

- `pnpm exec vitest run --config tests/restart-chaos/vitest.config.ts`:
295 passed.
- `node scripts/typecheck-test-projects.mjs tests/restart-chaos`:
passed.
- Live local waves, codex, one wave at a time (shared machine lock),
`--concurrency 2`, at most 3 tasks:

| Wave | Server build | Command | Result |
| --- | --- | --- | --- |
| `w-20261110T024308-s106` | this branch (master `5570debc5b` base) |
`--workloads Q3 --faults K1 --matrix --seed 106` | 3/3 pass (K0,
K1^asked, K1^answered); the app saw one refresh before each decision and
one `tools/call` |
| `w-20261110T024447-s106` | `--server-from` parent of #20340
(`0e325e8d52`) | same | 3/3 fail as expected: approve returns 409
`action_expired` ("Tool review has expired"), no `tools/call`, task
stays `in_review` |
| `w-20261110T024644-s107` | this branch | `--workloads Q2 --faults K1
--matrix --seed 107` | K1^asked and K1^answered pass. The K0 cell failed
only because the oracle then required a comment; the agent had reported
`ANSWER-…` in its bounded turn's run result 14 s after the answer, while
blocked. The oracle now accepts either. |
| `w-20261110T030341-s107` | `--server-from` parent of #20363
(`eebdfe71b1`) | `--workloads Q2 --faults K1 --matrix --seed 107` | 3/3
fail as expected: `answer_reached_agent_while_blocked`. No run starts
between the answer and the finished prerequisite (6 minutes of ready
server). |
| `w-20261110T033426-s108` | this branch, rebased onto #20432 |
`--workloads Q1,Q2 --faults K1 --triggers asked,answered --tasks 4
--seed 108` | Q1^asked and Q2^asked pass. Q1^answered and Q2^answered
fail on a product gap (below). |
| `w-20261110T020037-s102` | this branch, before the rebase |
`--workloads Q1,Q2,Q3 --faults K1 --triggers asked,answered --tasks 3
--seed 102` | Q1 with `asked` and `answered` K1 restarts: both done with
the answer. |

Waves `…-s107` (pre-fix) and `…-s106` (pre-fix) used the same harness
code as the green waves. Only the server build changed.

**New product failure (not fixed here).** A K1 restart 0.3–3 s after the
board's answer can lose the answer turn's native run:

- `w-20261110T033426-s108/t04` (Q1, K1 2.9 s after the answer): run
`a40cc8f7` resumes with `resume_dead_runner`, then fails
`runner_state_identity_mismatch: authority_indeterminate`
(`adapter_failed`). The task shows "Couldn't finish this attempt. Your
message is saved." and needs Retry. Retry works.
- `w-20261110T033426-s108/t03` (Q2, blocked task, K1 0.27 s after the
answer): run `5b5355de` is `process_lost`. Its bounded retry `92a57f50`
fails `native_session_recovery_failed` (`session.snapshot` failed). The
Retry click starts `0e6aa9d1`, which fails
`runner_state_identity_mismatch:
prior_owner_terminal_state_indeterminate;
retirement_refused=journal_commands_unsettled(turn.start.failed)`. The
task then waits with `retryWait.reason=process_identity_missing` and
stays blocked without the answer.
- Suspected path: native runner restart reconciliation of the runner
state identity (`resume_dead_runner`, journal command settlement) in
`server/src/services/native-runtime/` and `packages/paperclip-runner/`.
Host load was 8–14 during the wave.

## Risks

- Test harness only. No product code changes.
- Q2 depends on the model following its instructions. The oracle accepts
the answer in either a comment or the bounded turn's run result, because
both were seen.
- Q3 shifts persisted timestamps only in the wave's isolated database.
It does not shift unrelated tables, so it simulates elapsed time for the
review and the token, not for the whole instance.
- `K0` and the new triggers add values to shared registries (`types.ts`,
`scheduler.ts`). Existing plans keep their meaning.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude
Code with tool use and code execution.

## 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-09 22:37:24 -06:00
..