mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 19:28:46 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The native Runner supervises the harness and tool processes for each run > - A launcher can start Runner with a negative `oom_score_adj`, and Linux children inherit that value > - Then a large tool process has the same protection as Runner > - If that protection is -1000, the kernel cannot kill the process, and a VM with no other candidate panics > - The correct protection for Runner is different in each environment, so one static value is not correct > - This pull request adds an optional `--oom-score-adj` launch flag. With the flag, Runner also sets supervised processes to 0 when Runner has a negative score > - The benefit is that the launcher controls Runner protection, and the kernel can kill the large process and let Runner report the failure ## Linked Issues or Issue Description Related: #14509 and #16844. These pull requests change scores for the server and for sandbox tool commands. This pull request changes the native Runner. **What happened?** A native Runner on a VM inherited `oom_score_adj` -1000 from the VM init process. A typecheck under Runner used most of the VM memory. The kernel could not select any process and panicked with "System is deadlocked on memory". The VM restarted, and the run failed with a transport error. **Expected behavior** The kernel can kill the large tool process. Runner continues and reports the result. **Steps to reproduce** Start Runner with a negative score. Start a supervised process. Read `/proc/<pid>/oom_score_adj` for the supervised process. The value is the same as the Runner value. After this change, the value is 0 when the launcher gives the flag. **Deployment mode** Native Runner on Linux, when the launcher gives Runner a negative score. ## What Changed - Add the optional `--oom-score-adj <-1000..=1000>` flag to `paperclip-runnerd`. Runner has no default value. - Stop Runner startup when the value is not valid. When the kernel refuses a valid value, or the platform is not Linux, write a diagnostic and continue, because Runner is then only less protected than the launcher asked for. - When the launcher gives the flag and the Runner score is negative, set each supervised process and verified stdio MCP server to 0 after spawn. - Without the flag, Runner does not change any score. Children inherit the Runner score, as they do now. - Add unit and integration tests. ## Verification Tests ran in `rust:1-bookworm` (rustc 1.99.1) on Linux arm64 with `CAP_SYS_RESOURCE`: - `cargo fmt --check -p paperclip-runner-core` - `cargo test -p paperclip-runner-core --no-fail-fast`, before the flag gate commit: all targets pass except `codex_provider` and `stdio_mcp`. - `provider_receives_the_isolated_codex_auth_home` and its subprocess helper fail in this container. They fail in the same way on clean `master`. - `receipt_limit_polls_an_authoritative_terminal_with_unacknowledged_events` and `cancellation_waiting_behind_output_is_handled_before_new_dispatch` failed once in the full run. They pass when they run alone, on this branch and on clean `master`. I think that they are sensitive to load, but I did not prove this. CI is the check for these tests. - `--test oom_score` and `--test oom_score_without_flag` pass with and without `CAP_SYS_RESOURCE`. It covers the real Runner binary, flags that are not valid, a Runner that cannot apply a negative score, and a supervised child of a Runner with a negative score. The tests for children of a Runner with a negative score skip themselves without `CAP_SYS_RESOURCE`. The unit test covers the same reset logic without that capability. I also ran the debug Runner binary through the agent computer `enter` command in a container, as UID 1000, with a floor of -500: - Runner stayed alive at -500. - The `oom_score` integration test passed in the same setup. Its supervised child had the score 0. After the flag gate commit, I ran the library `oom_score` tests, `--test oom_score`, `--test oom_score_without_flag`, and `--test process_supervisor` again. CI runs the complete suite. I did not run this on a real VM. ## Risks - A fork in the short time after exec and before the write keeps the Runner score. The kernel can still kill that process unless the score is -1000. - The write to the child can fail. Runner ignores this failure, because the child has no more protection than Runner. - A launcher without the necessary privilege gets a Runner at the inherited score. Runner writes a diagnostic to standard error, and the run continues. A launcher that discards standard error does not see the diagnostic. - A launcher that does not give the flag has no change, also when Runner starts with a negative score. On Linux, each spawn has one more atomic read and no system call. A `oom_score_without_flag` test binary checks this. - A new Runner under a launcher that does not give the flag and gives Runner -1000 keeps its children at -1000. Update that launcher to give the flag. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in 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 - [x] 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>