cli-agent-runner 0.3.11__tar.gz → 0.3.13__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (411) hide show
  1. cli_agent_runner-0.3.13/.ralph/items.json +86 -0
  2. cli_agent_runner-0.3.13/.ralph/progress.md +47 -0
  3. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.vulture-whitelist.py +22 -0
  4. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CHANGELOG.md +22 -0
  5. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/PKG-INFO +1 -1
  6. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/SECURITY.md +6 -0
  7. cli_agent_runner-0.3.13/agent_runner/_bounded.py +135 -0
  8. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_docgen.py +14 -1
  9. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/memory.py +27 -3
  10. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_lifecycle.py +1 -1
  11. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_serve_policy.py +13 -5
  12. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_version.py +2 -2
  13. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/_serve_cgroup.py +9 -2
  14. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/__init__.py +7 -1
  15. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/loader.py +107 -0
  16. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/models.py +71 -0
  17. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/parsers.py +59 -1
  18. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/events.py +2 -0
  19. cli_agent_runner-0.3.13/agent_runner/goal.py +428 -0
  20. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/metrics.py +28 -12
  21. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/runner.py +33 -1
  22. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/service_unit.py +3 -1
  23. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/vcs_state.py +12 -28
  24. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/architecture.md +2 -0
  25. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/configuration.md +113 -1
  26. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/memory-efficiency.md +89 -0
  27. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/pyproject.toml +2 -1
  28. cli_agent_runner-0.3.13/tests/_clock.py +95 -0
  29. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/conftest.py +295 -175
  30. cli_agent_runner-0.3.13/tests/e2e/growth_child.py +28 -0
  31. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_pre_oom.py +179 -19
  32. cli_agent_runner-0.3.13/tests/integration/test_goal_steering_e2e.py +479 -0
  33. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_spawn_round_mem_floor.py +26 -50
  34. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_config_error_consistency.py +1 -0
  35. cli_agent_runner-0.3.13/tests/invariants/test_goal_firewall.py +166 -0
  36. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_import_footprint.py +5 -0
  37. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_module_boundaries.py +32 -5
  38. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_vacuous_invariants.py +1 -0
  39. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_timeout_budget_invariant.py +27 -0
  40. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/linux/test_memory_high_nudge_e2e.py +4 -4
  41. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cgroup_probe.py +30 -0
  42. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config.py +438 -1
  43. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_docgen.py +1 -1
  44. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_git_timeout.py +9 -4
  45. cli_agent_runner-0.3.13/tests/unit/test_goal_assessor.py +264 -0
  46. cli_agent_runner-0.3.13/tests/unit/test_goal_executor.py +222 -0
  47. cli_agent_runner-0.3.13/tests/unit/test_goal_firewall_behavioral.py +173 -0
  48. cli_agent_runner-0.3.13/tests/unit/test_goal_ledger.py +136 -0
  49. cli_agent_runner-0.3.13/tests/unit/test_growth_child_constants.py +8 -0
  50. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_host_health.py +20 -0
  51. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics.py +5 -2
  52. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_outer_round_ceiling.py +37 -0
  53. cli_agent_runner-0.3.13/tests/unit/test_run_bounded.py +133 -0
  54. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner.py +86 -0
  55. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_cgroup_spine.py +60 -32
  56. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_service_unit.py +35 -0
  57. cli_agent_runner-0.3.11/tests/_clock.py +0 -53
  58. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.codecov.yml +0 -0
  59. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  60. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  61. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  62. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  63. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/dependabot.yml +0 -0
  64. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/workflows/ci.yml +0 -0
  65. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.github/workflows/release.yml +0 -0
  66. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/.gitignore +0 -0
  67. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/BACKLOG.md +0 -0
  68. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CODE_OF_CONDUCT.md +0 -0
  69. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/CONTRIBUTING.md +0 -0
  70. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/LICENSE +0 -0
  71. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/README.md +0 -0
  72. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/README.zh.md +0 -0
  73. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/__init__.py +0 -0
  74. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/__init__.py +0 -0
  75. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/agent.py +0 -0
  76. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/rounds.py +0 -0
  77. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_emit/serve.py +0 -0
  78. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_install.py +0 -0
  79. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_detectors.py +0 -0
  80. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_registry.py +0 -0
  81. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_monitor_state.py +0 -0
  82. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_notify.py +0 -0
  83. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_observe.py +0 -0
  84. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_plugin_manifest.py +0 -0
  85. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_plugin_scan.py +0 -0
  86. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_procwait.py +0 -0
  87. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_redact.py +0 -0
  88. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_registry.py +0 -0
  89. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_resolve.py +0 -0
  90. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_round_outcome.py +0 -0
  91. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_round_support.py +0 -0
  92. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_substrate.py +0 -0
  93. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/_throttle.py +0 -0
  94. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/agent_runtime.py +0 -0
  95. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/api.py +0 -0
  96. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/api_types.py +0 -0
  97. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/__init__.py +0 -0
  98. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/_constants.py +0 -0
  99. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/claude_rate_limit.py +0 -0
  100. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/codewhale.py +0 -0
  101. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/gemini.py +0 -0
  102. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/kimi.py +0 -0
  103. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/builtin_plugins/pi.py +0 -0
  104. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/__init__.py +0 -0
  105. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/__main__.py +0 -0
  106. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/_serve_round.py +0 -0
  107. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/common.py +0 -0
  108. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/doctor_cmd.py +0 -0
  109. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/events_cmd.py +0 -0
  110. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/init_cmd.py +0 -0
  111. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/install_cmd.py +0 -0
  112. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/migrate_cmd.py +0 -0
  113. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/monitor_cmd.py +0 -0
  114. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/peek_cmd.py +0 -0
  115. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/round_cmd.py +0 -0
  116. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/serve_cmd.py +0 -0
  117. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/service_cmd.py +0 -0
  118. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/cli/upgrade_cmd.py +0 -0
  119. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/clock.py +0 -0
  120. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/errors.py +0 -0
  121. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/config/validators.py +0 -0
  122. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/context_store.py +0 -0
  123. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/defenses.py +0 -0
  124. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/event_log.py +0 -0
  125. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/hooks.py +0 -0
  126. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/host_health.py +0 -0
  127. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/http_progress.py +0 -0
  128. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/lifecycle.py +0 -0
  129. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/migrations.py +0 -0
  130. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/monitor.py +0 -0
  131. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/phase_select.py +0 -0
  132. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/__init__.py +0 -0
  133. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/aider.toml +0 -0
  134. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/claude.toml +0 -0
  135. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/codewhale.toml +0 -0
  136. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/gemini.toml +0 -0
  137. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/kimi.toml +0 -0
  138. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/presets/pi.toml +0 -0
  139. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/prompt_loader.py +0 -0
  140. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/remote_relay.py +0 -0
  141. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/round_log.py +0 -0
  142. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/round_view.py +0 -0
  143. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/scaffold.py +0 -0
  144. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/schedule.py +0 -0
  145. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/agent_runner/startup_check.py +0 -0
  146. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/build.sh +0 -0
  147. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/deploy/Dockerfile +0 -0
  148. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/deploy/docker-compose.yml +0 -0
  149. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/README.md +0 -0
  150. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/commands.md +0 -0
  151. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/events.md +0 -0
  152. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/long-running-agents.md +0 -0
  153. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/migrations/0.2.md +0 -0
  154. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/migrations/0.3.md +0 -0
  155. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/plugins.md +0 -0
  156. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/quickstart.md +0 -0
  157. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/aider.md +0 -0
  158. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/codewhale.md +0 -0
  159. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/container-pi.md +0 -0
  160. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/deploy-all-in-one.md +0 -0
  161. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/kimi.md +0 -0
  162. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/recipes/pi.md +0 -0
  163. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/runbook.md +0 -0
  164. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/docs/thesis.md +0 -0
  165. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/__init__.py +0 -0
  166. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/_test_helpers.py +0 -0
  167. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/conftest.py +0 -0
  168. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/contract/__init__.py +0 -0
  169. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/contract/test_public_api_surface.py +0 -0
  170. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/__init__.py +0 -0
  171. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_graceful_stop.py +0 -0
  172. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_install_systemd.py +0 -0
  173. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_monitor_remote.py +0 -0
  174. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_pi_resume.py +0 -0
  175. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/e2e/test_e2e_round_lifecycle.py +0 -0
  176. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/fixtures/cli-real-output/claude-2.1.143-assistant-tool-use.jsonl +0 -0
  177. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/fixtures/cli-real-output/claude-2.1.143-result-event.jsonl +0 -0
  178. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/fixtures/cli-real-output/gemini-0.42.0-result-event.jsonl +0 -0
  179. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/generate_vulture_whitelist.py +0 -0
  180. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/__init__.py +0 -0
  181. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_auto_stop_e2e.py +0 -0
  182. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_bounded_run.py +0 -0
  183. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_cooperative_grace_property.py +0 -0
  184. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_cooperative_stop_signal.py +0 -0
  185. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_fresh_eyes_signal.py +0 -0
  186. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_grace_kill_emission.py +0 -0
  187. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_install_dry_run.py +0 -0
  188. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_monitor_seeded.py +0 -0
  189. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_plugin_real_flow.py +0 -0
  190. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_prompt_delivery_stdin.py +0 -0
  191. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_reap_property.py +0 -0
  192. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_run_one_round_with_fake_agent.py +0 -0
  193. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_scaffold_presets.py +0 -0
  194. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_serve_loop.py +0 -0
  195. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_spawn_round_wedged.py +0 -0
  196. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_substrate_fingerprint.py +0 -0
  197. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/integration/test_transient_error_backoff.py +0 -0
  198. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/__init__.py +0 -0
  199. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/_docs.py +0 -0
  200. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/_event_scan.py +0 -0
  201. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_architecture.py +0 -0
  202. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_atomic_write_enforced.py +0 -0
  203. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_authored_facts_declared.py +0 -0
  204. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_auto_stop_policy_ssot.py +0 -0
  205. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_bdd_test_conventions.py +0 -0
  206. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_brake_defaults_off.py +0 -0
  207. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_catalogs.py +0 -0
  208. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_classification_ssot.py +0 -0
  209. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_config_value_set_ssot.py +0 -0
  210. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_container_stop_bound.py +0 -0
  211. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_dataclass_fields_have_producers.py +0 -0
  212. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_builtin_plugins.py +0 -0
  213. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_claims_match_ssot.py +0 -0
  214. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_cli_claims.py +0 -0
  215. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_operator_surface.py +0 -0
  216. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_test_pointers_exist.py +0 -0
  217. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_transient_error_claims.py +0 -0
  218. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_doc_usage_event_fields.py +0 -0
  219. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_docs_generated.py +0 -0
  220. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_docs_index_complete.py +0 -0
  221. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_entry_points_resolve.py +0 -0
  222. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_int_coercion.py +0 -0
  223. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_kind_registry.py +0 -0
  224. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_event_kinds_ssot.py +0 -0
  225. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_events_doc_contract.py +0 -0
  226. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_gen_blocks_name_their_source.py +0 -0
  227. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_hard_wall_signal.py +0 -0
  228. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_hook_contract_docs.py +0 -0
  229. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_layer_2_loop_size.py +0 -0
  230. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_migrations_registry_ssot.py +0 -0
  231. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_module_sizes.py +0 -0
  232. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_monitor_self_exclusion.py +0 -0
  233. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_ai_signatures.py +0 -0
  234. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_asyncio_select.py +0 -0
  235. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_direct_inspect_import.py +0 -0
  236. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_internal_refs.py +0 -0
  237. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_price_logic.py +0 -0
  238. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_pytest_skip_on_parse_fail.py +0 -0
  239. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_raw_time.py +0 -0
  240. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_no_version_refs.py +0 -0
  241. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_peek_schema_version.py +0 -0
  242. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_phase_select_stateless.py +0 -0
  243. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_plugins_config_stable.py +0 -0
  244. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_procwait_shield.py +0 -0
  245. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_readme_zh_is_pointer.py +0 -0
  246. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_repo_constants_patched_in_tests.py +0 -0
  247. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_alloc_growth.py +0 -0
  248. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_log_decode_boundary.py +0 -0
  249. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_num_single_source.py +0 -0
  250. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_round_result_stable.py +0 -0
  251. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_set_diff_for_auto_tool_classification.py +0 -0
  252. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_stash_uses_sha_not_index.py +0 -0
  253. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_throttle_stateless_imports.py +0 -0
  254. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_upstream_schema_canary.py +0 -0
  255. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/invariants/test_vulture_whitelist_generated.py +0 -0
  256. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/linux/__init__.py +0 -0
  257. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/__init__.py +0 -0
  258. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/parser.py +0 -0
  259. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/test_parser.py +0 -0
  260. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/literate/test_quickstart.py +0 -0
  261. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/__init__.py +0 -0
  262. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime.py +0 -0
  263. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_container.py +0 -0
  264. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_grace.py +0 -0
  265. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_marker_scan.py +0 -0
  266. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_progress.py +0 -0
  267. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_reap.py +0 -0
  268. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_agent_runtime_signal_name.py +0 -0
  269. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_assemble_prompt.py +0 -0
  270. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_events_stream.py +0 -0
  271. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_install.py +0 -0
  272. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_observation.py +0 -0
  273. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_read_round_num.py +0 -0
  274. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_resolve_phase.py +0 -0
  275. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_service.py +0 -0
  276. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_api_types.py +0 -0
  277. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_apply_reap_grace_env.py +0 -0
  278. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_apply_resume_env.py +0 -0
  279. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_auto_stop_gating.py +0 -0
  280. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_catalogs.py +0 -0
  281. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_claude_error_detector.py +0 -0
  282. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_claude_foreign_schema.py +0 -0
  283. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli.py +0 -0
  284. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_common.py +0 -0
  285. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_init_install.py +0 -0
  286. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_main_boundary.py +0 -0
  287. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_monitor_http.py +0 -0
  288. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_service_peek_monitor.py +0 -0
  289. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cli_upgrade.py +0 -0
  290. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_codewhale_code_precedence.py +0 -0
  291. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_codewhale_plugin.py +0 -0
  292. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_coerce_int_poison.py +0 -0
  293. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_fresh_eyes.py +0 -0
  294. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_max_rounds.py +0 -0
  295. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_nested_strictness.py +0 -0
  296. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_prompt_delivery.py +0 -0
  297. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_resume_guard.py +0 -0
  298. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_schedule.py +0 -0
  299. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_stop_file.py +0 -0
  300. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_strictness.py +0 -0
  301. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_substrate_fingerprint_paths.py +0 -0
  302. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_config_transient_error_action.py +0 -0
  303. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_context_store.py +0 -0
  304. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_control_char_config.py +0 -0
  305. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_cooperative_signal_env.py +0 -0
  306. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_defenses.py +0 -0
  307. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_detect_hung_latch.py +0 -0
  308. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_detector_isolation.py +0 -0
  309. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_doctor_cmd.py +0 -0
  310. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_effective_throttle_view.py +0 -0
  311. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_config_migrated.py +0 -0
  312. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_schedule.py +0 -0
  313. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_emit_schedule_phase_skipped.py +0 -0
  314. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_event_log.py +0 -0
  315. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events.py +0 -0
  316. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd.py +0 -0
  317. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd_rollover.py +0 -0
  318. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_cmd_tail.py +0 -0
  319. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_events_jsonl_decode.py +0 -0
  320. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_example_plugin.py +0 -0
  321. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exec_prefix.py +0 -0
  322. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exit_cause.py +0 -0
  323. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_exit_classifier.py +0 -0
  324. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_fresh_eyes_trigger.py +0 -0
  325. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_gemini_plugin.py +0 -0
  326. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_hook_redact_order.py +0 -0
  327. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_hooks.py +0 -0
  328. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_http_progress.py +0 -0
  329. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_http_progress_epoch.py +0 -0
  330. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_init_entry_points.py +0 -0
  331. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_interruptible_sleep_should_stop.py +0 -0
  332. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_kill_pgroup_shielding.py +0 -0
  333. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_kimi_plugin.py +0 -0
  334. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_lifecycle.py +0 -0
  335. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_loader_split.py +0 -0
  336. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics_cgroup_usage.py +0 -0
  337. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_metrics_swap.py +0 -0
  338. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_migrate_cmd.py +0 -0
  339. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_migrations.py +0 -0
  340. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_assembly.py +0 -0
  341. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_cmd_severity.py +0 -0
  342. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_dedup_identity.py +0 -0
  343. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_dedup_rearm.py +0 -0
  344. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_anomaly_repetitive.py +0 -0
  345. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_rate_limit.py +0 -0
  346. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detect_supervisor_stale.py +0 -0
  347. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_detectors.py +0 -0
  348. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_event_tail.py +0 -0
  349. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_loop.py +0 -0
  350. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_on_alert.py +0 -0
  351. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_remote.py +0 -0
  352. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_round_deferred_stale.py +0 -0
  353. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_monitor_schedule_pause.py +0 -0
  354. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_network_blip_signal_guard.py +0 -0
  355. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_notify.py +0 -0
  356. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_open_round_log.py +0 -0
  357. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_orphan_chain_single_pass.py +0 -0
  358. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_argparse.py +0 -0
  359. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_project_isolation.py +0 -0
  360. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_schedule.py +0 -0
  361. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_peek_select.py +0 -0
  362. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_agent_launch.py +0 -0
  363. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_profiles.py +0 -0
  364. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_phase_select.py +0 -0
  365. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_pi_plugin.py +0 -0
  366. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_constants.py +0 -0
  367. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_manifest.py +0 -0
  368. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugin_scan_parity.py +0 -0
  369. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_plugins_config_fields.py +0 -0
  370. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_presets.py +0 -0
  371. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_procwait.py +0 -0
  372. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_prompt_loader.py +0 -0
  373. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_read_json_hardening.py +0 -0
  374. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_redact.py +0 -0
  375. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_resolve.py +0 -0
  376. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_resolve_dirty_tree.py +0 -0
  377. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_cmd.py +0 -0
  378. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_cmd_sigterm.py +0 -0
  379. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_log_helpers.py +0 -0
  380. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_outcome.py +0 -0
  381. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_outcome_agent_axis.py +0 -0
  382. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_round_view.py +0 -0
  383. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner_sidecar_ordering.py +0 -0
  384. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_runner_throttle.py +0 -0
  385. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_scaffold.py +0 -0
  386. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_schedule.py +0 -0
  387. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_cmd_bounded.py +0 -0
  388. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_config_broken.py +0 -0
  389. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_crash_loop.py +0 -0
  390. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_mem_gate.py +0 -0
  391. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_phase_schedule.py +0 -0
  392. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_policy.py +0 -0
  393. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round.py +0 -0
  394. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_log.py +0 -0
  395. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_oom_killed.py +0 -0
  396. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_terminate.py +0 -0
  397. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_round_wait_exit.py +0 -0
  398. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_schedule_gate.py +0 -0
  399. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_sentinel.py +0 -0
  400. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_serve_wakeup.py +0 -0
  401. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_service_mode.py +0 -0
  402. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_startup_battery_exit.py +0 -0
  403. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_startup_check.py +0 -0
  404. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_substrate.py +0 -0
  405. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_supervisor_stale_self_blind.py +0 -0
  406. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_terminate_round_wait_exit.py +0 -0
  407. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_back_off_ladder.py +0 -0
  408. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_epoch_bound.py +0 -0
  409. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_throttle_plugin_classification.py +0 -0
  410. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_upgrade_migrate.py +0 -0
  411. {cli_agent_runner-0.3.11 → cli_agent_runner-0.3.13}/tests/unit/test_vcs_state.py +0 -0
@@ -0,0 +1,86 @@
1
+ {
2
+ "version": 1,
3
+ "runtime_contract": {
4
+ "source_docs": [],
5
+ "verification_gates": [
6
+ {
7
+ "name": "ruff",
8
+ "command": ".venv/bin/ruff check . && .venv/bin/ruff format --check ."
9
+ }
10
+ ],
11
+ "require_progress_append": true,
12
+ "require_one_item_per_iteration": true,
13
+ "require_commit": true
14
+ },
15
+ "items": [
16
+ {
17
+ "category": "functional",
18
+ "description": "metrics.sample() always includes psi_full_total, io_psi_some_avg10, and io_psi_full_avg10 (None when unread). _read_psi returns (some_avg10, full_avg10, full_total). host_health.memory_pressure verdict and context stay identical when those extra keys are injected.",
19
+ "steps": [
20
+ "Add or update tests in tests/unit/test_metrics.py for the triple return and the three sample keys.",
21
+ "Add tests/unit/test_host_health.py coverage that a PSI-healthy sample plus huge io_psi_* / psi_full_total yields the same Pressure as the sample without those keys.",
22
+ "Implement parsing of full-line total= and a second _read_psi call on /proc/pressure/io inside sample(); do not edit host_health.py.",
23
+ "Run .venv/bin/python -m pytest tests/unit/test_metrics.py tests/unit/test_host_health.py tests/unit/test_monitor_detectors.py -q and the ruff gate."
24
+ ],
25
+ "passes": true,
26
+ "regression_notes": "76 passed (test_metrics + test_host_health + test_monitor_detectors); ruff check+format clean. host_health.py untouched. Darwin sample keys present as None."
27
+ },
28
+ {
29
+ "category": "functional",
30
+ "description": "host_cgroup_memory_limit events carry defer as a bool equal to _probe_and_emit_cgroup_defer's return value (True when both caps are finite and plausible, including MemorySwapMax=0).",
31
+ "steps": [
32
+ "Add tests/unit/test_cgroup_probe.py cases: memory_swap_max=0 with finite memory_max under host RAM returns True and emits defer true; memory_swap_max missing returns False and emits defer false.",
33
+ "Add defer: bool to emit_host_cgroup_memory_limit and pass the already-computed defer flag from _probe_and_emit_cgroup_defer.",
34
+ "Point the existing tests/e2e/test_e2e_pre_oom.py diagnostic at host_cgroup_memory_limit.defer.",
35
+ "Run .venv/bin/python -m pytest tests/unit/test_cgroup_probe.py -q and the ruff gate."
36
+ ],
37
+ "passes": true,
38
+ "regression_notes": "55 passed (test_cgroup_probe); ruff check+format clean. MemorySwapMax=0 emits defer true; missing swap.max emits defer false. D-2 diagnostic names host_cgroup_memory_limit.defer."
39
+ },
40
+ {
41
+ "category": "functional",
42
+ "description": "round_cgroup_memory may carry optional flat io_psi_some_avg10, io_psi_full_avg10, and psi_full_total from metrics.sample() at emit time. They are omitted when unread. They are never copied into Pressure.context.",
43
+ "steps": [
44
+ "Extend tests/unit/test_serve_round_cgroup_spine.py to monkeypatch metrics.sample and assert the three fields on the emitted round_cgroup_memory event.",
45
+ "Thread optional kwargs through emit_round_cgroup_memory and _emit_round_cgroup_memory; call sample() once at emit.",
46
+ "Run .venv/bin/python -m pytest tests/unit/test_serve_round_cgroup_spine.py tests/unit/test_serve_round_oom_killed.py -q and the ruff gate."
47
+ ],
48
+ "passes": true,
49
+ "regression_notes": "19 passed (test_serve_round_cgroup_spine + test_serve_round_oom_killed); ruff check+format clean. Optional PSI fields omitted when unread/None; present when sample() read them. Not copied into Pressure.context."
50
+ },
51
+ {
52
+ "category": "functional",
53
+ "description": "The paced pre-OOM child lives in tests/e2e/growth_child.py (8 MiB every 2.5s, page-touch). pi_growth_script installs that file instead of an inlined string. Importing the module does not start the infinite loop.",
54
+ "steps": [
55
+ "Create tests/e2e/growth_child.py with the existing constants and main() behind if __name__.",
56
+ "Add a constants test that imports the module and asserts _CHUNK_BYTES and _PACE_S.",
57
+ "Change tests/e2e/conftest.py pi_growth_script to ship that file.",
58
+ "Run the constants test plus the ruff gate."
59
+ ],
60
+ "passes": true,
61
+ "regression_notes": "1 passed (test_growth_child_constants); ruff check+format clean. pi_growth_script ships tests/e2e/growth_child.py via base64; import does not start the loop."
62
+ },
63
+ {
64
+ "category": "functional",
65
+ "description": "A skip-closed control-arm e2e exists: same unit as treatment except MemorySwapMax=0, MemoryHigh=120M kept, no oom.group and no OOMPolicy=kill. Unset AGENT_RUNNER_E2E_CGROUP and AGENT_RUNNER_E2E_PI, the new test skips. Assertions in source require defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers.",
66
+ "steps": [
67
+ "Factor the systemd unit body so swap max is a parameter. Control uses 0. Do not add OOMPolicy or memory.oom.group.",
68
+ "Add the control-arm test and shared assertions. Gate with existing PI flag and/or AGENT_RUNNER_E2E_CGROUP. Control wait ceiling 180s.",
69
+ "Without those env flags, run .venv/bin/python -m pytest tests/e2e/test_e2e_pre_oom.py -q and confirm skip (not fail, not pass). Do not export the e2e flags. Run the ruff gate."
70
+ ],
71
+ "passes": true,
72
+ "regression_notes": "3 skipped (test_e2e_pre_oom.py without AGENT_RUNNER_E2E_CGROUP/PI); ruff check+format clean. Control unit MemorySwapMax=0, MemoryHigh=120M kept, no oom.group/OOMPolicy. Assertions in source: defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers."
73
+ },
74
+ {
75
+ "category": "functional",
76
+ "description": "Root SECURITY.md gains one paragraph: isolation is the operator's job (exec_prefix, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety. CHANGELOG adds ## [0.3.13] - UNRELEASED. No docs/SECURITY.md. Optional results template under gitignored docs/internal/notes is allowed and must not be staged.",
77
+ "steps": [
78
+ "Edit SECURITY.md Containment or Threat model with the reframe paragraph. Keep existing recipe links.",
79
+ "Insert CHANGELOG ## [0.3.13] - UNRELEASED above 0.3.12. Do not claim uncapped-before-coma.",
80
+ "Confirm docs/SECURITY.md does not exist. Run tests/invariants/test_docs_index_complete.py tests/invariants/test_doc_claims_match_ssot.py and the ruff gate."
81
+ ],
82
+ "passes": true,
83
+ "regression_notes": "4 passed (test_docs_index_complete + test_doc_claims_match_ssot); ruff check+format clean. docs/SECURITY.md absent. CHANGELOG [0.3.13] UNRELEASED; no uncapped-before-coma claim. Results template gitignored, not staged."
84
+ }
85
+ ]
86
+ }
@@ -0,0 +1,47 @@
1
+ ## 2026-09-18 sample() IO-PSI + psi_full_total
2
+
3
+ - item: metrics.sample() always includes psi_full_total, io_psi_some_avg10, and io_psi_full_avg10 (None when unread). _read_psi returns (some_avg10, full_avg10, full_total). host_health.memory_pressure verdict and context stay identical when those extra keys are injected.
4
+ - rationale: plan prefers ladder-blindness before defer-field / e2e fixture work. First unfinished item.
5
+ - files: agent_runner/metrics.py, tests/unit/test_metrics.py, tests/unit/test_host_health.py
6
+ - verification: `.venv/bin/python -m pytest tests/unit/test_metrics.py tests/unit/test_host_health.py tests/unit/test_monitor_detectors.py -q` → 76 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
7
+ - notes: did not edit host_health.py or _NEW_MEM_SAMPLE_KEYS. Pre-existing pi-lens findings in cgroup_delegated / _brake_high_value left untouched (outside this item).
8
+
9
+ ## 2026-09-18 host_cgroup_memory_limit.defer bool
10
+
11
+ - item: host_cgroup_memory_limit events carry defer as a bool equal to _probe_and_emit_cgroup_defer's return value (True when both caps are finite and plausible, including MemorySwapMax=0).
12
+ - rationale: plan prefers locking the defer-field contract before e2e fixture work. First remaining unfinished item after sample() IO-PSI.
13
+ - files: agent_runner/_emit/memory.py, agent_runner/cli/_serve_cgroup.py, tests/unit/test_cgroup_probe.py, tests/e2e/test_e2e_pre_oom.py
14
+ - verification: `.venv/bin/python -m pytest tests/unit/test_cgroup_probe.py -q` → 55 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
15
+ - notes: defer is a required emit kwarg (every probe is yes/no). Did not touch runbook.md (docs item). Pre-existing pi-lens typing findings in _serve_cgroup left untouched.
16
+
17
+ ## 2026-09-18 round_cgroup_memory corroborating PSI fields
18
+
19
+ - item: round_cgroup_memory may carry optional flat io_psi_some_avg10, io_psi_full_avg10, and psi_full_total from metrics.sample() at emit time. They are omitted when unread. They are never copied into Pressure.context.
20
+ - rationale: plan prefers locking ladder-blindness and defer-field contracts before e2e fixture work. First remaining unfinished item after host_cgroup_memory_limit.defer.
21
+ - files: agent_runner/_emit/memory.py, agent_runner/cli/_serve_cgroup.py, tests/unit/test_serve_round_cgroup_spine.py
22
+ - verification: `.venv/bin/python -m pytest tests/unit/test_serve_round_cgroup_spine.py tests/unit/test_serve_round_oom_killed.py -q` → 19 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
23
+ - notes: assumed sample() runs only on the successful emit path (skip-no-op paths do not sample); each None key is omitted independently. Did not edit host_health.py, events.md, or runbook.md. Pre-existing pi-lens typing findings in _serve_cgroup left untouched.
24
+
25
+ ## 2026-09-18 extract growth_child.py
26
+
27
+ - item: The paced pre-OOM child lives in tests/e2e/growth_child.py (8 MiB every 2.5s, page-touch). pi_growth_script installs that file instead of an inlined string. Importing the module does not start the infinite loop.
28
+ - rationale: plan prefers extracting growth_child.py after ladder-blindness/defer-field contracts, before skip-closed control tests. First remaining unfinished item.
29
+ - files: tests/e2e/growth_child.py, tests/e2e/conftest.py, tests/unit/test_growth_child_constants.py
30
+ - verification: `.venv/bin/python -m pytest tests/unit/test_growth_child_constants.py -q` → 1 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
31
+ - notes: assumed the constants test lives under tests/unit because CI and build.sh --ignore=tests/e2e. pi_growth_script still base64-over-ssh (same install path), reading tests/e2e/growth_child.py bytes instead of an inlined string. Importing the module does not enter main().
32
+
33
+ ## 2026-09-18 skip-closed control-arm e2e
34
+
35
+ - item: A skip-closed control-arm e2e exists: same unit as treatment except MemorySwapMax=0, MemoryHigh=120M kept, no oom.group and no OOMPolicy=kill. Unset AGENT_RUNNER_E2E_CGROUP and AGENT_RUNNER_E2E_PI, the new test skips. Assertions in source require defer true, no round_mem_terminated, no memory_high_engaged, events_oom_kill_delta>=1 or INVALID, DEFER-FIRST or KERNEL-FIRST named, growth child gone, host still answers.
36
+ - rationale: plan prefers skip-closed control tests after extracting growth_child.py. First remaining unfinished item.
37
+ - files: tests/e2e/conftest.py, tests/e2e/test_e2e_pre_oom.py
38
+ - verification: `.venv/bin/python -m pytest tests/e2e/test_e2e_pre_oom.py -q` → 3 skipped; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean
39
+ - notes: assumed PI control pytest timeout includes +600 install budget matching treatment; local uses +120 per plan. Two thin wrappers (PI + local CGROUP) share `_assert_control`. Skip-closed (unset env => skip) is the honest local proof; did not export e2e flags or run sudo/systemctl. Did not add OOMPolicy or memory.oom.group.
40
+
41
+ ## 2026-09-18 SECURITY reframe + CHANGELOG stub
42
+
43
+ - item: Root SECURITY.md gains one paragraph: isolation is the operator's job (exec_prefix, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety. CHANGELOG adds ## [0.3.13] - UNRELEASED. No docs/SECURITY.md. Optional results template under gitignored docs/internal/notes is allowed and must not be staged.
44
+ - rationale: plan says docs last. Last remaining unfinished item.
45
+ - files: SECURITY.md, CHANGELOG.md
46
+ - verification: `.venv/bin/python -m pytest tests/invariants/test_docs_index_complete.py tests/invariants/test_doc_claims_match_ssot.py -q` → 4 passed; `.venv/bin/ruff check . && .venv/bin/ruff format --check .` clean; `docs/SECURITY.md` absent
47
+ - notes: assumed not naming a competitor in SECURITY.md (Ralph item asks for the operator-vs-lifecycle split). Did not edit runbook.md (not in the Ralph item). Results template written under gitignored docs/internal/notes and not staged. Did not claim uncapped-before-coma or a live two-arm run.
@@ -6,6 +6,12 @@
6
6
  # bare name below marks one field "used". SSOT = the @dataclass definitions in
7
7
  # agent_runner/; a new field is covered by regenerating, never by editing here.
8
8
 
9
+ # agent_runner._bounded.BoundedResult
10
+ rc
11
+ stdout
12
+ stderr
13
+ timed_out
14
+
9
15
  # agent_runner._monitor_state.LocalSource
10
16
  log_dir
11
17
 
@@ -161,6 +167,11 @@ monitor
161
167
  phases
162
168
  plugins
163
169
  schedule
170
+ goal
171
+
172
+ # agent_runner.config.models.GoalConfig
173
+ ledger
174
+ checks
164
175
 
165
176
  # agent_runner.config.models.MonitorConfig
166
177
  auth_fail_patterns
@@ -232,6 +243,12 @@ pause_windows
232
243
  stash_idempotency_s
233
244
  dirty_action
234
245
 
246
+ # agent_runner.config.models._GoalCheckConfig
247
+ name
248
+ cmd
249
+ cwd
250
+ timeout_s
251
+
235
252
  # agent_runner.config.models._HostHealthBrakeConfig
236
253
  memory_high
237
254
  memory_high_step_pct
@@ -281,6 +298,11 @@ codifies
281
298
  guarded_by
282
299
  current_state
283
300
 
301
+ # agent_runner.goal.Advisory
302
+ observation
303
+ question
304
+ confidence
305
+
284
306
  # agent_runner.hooks.HookContext
285
307
  work_dir
286
308
  log_dir
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.13] - 2026-09-18
9
+
10
+ ### Added
11
+ - Two production cgroup modes are now checkable on the same paced growth child: treatment (`MemorySwapMax` infinity) still cooperatively terminates and reaps; control (`MemorySwapMax=0`) defers to kernel cgroup-OOM of the capped child. Both leave the host up. The uncapped-before-coma case stays inferred.
12
+ - `metrics.sample()` always includes corroborating `psi_full_total`, `io_psi_some_avg10`, and `io_psi_full_avg10` (`None` when unread). They are not kill rungs; `host_health.memory_pressure` stays blind to them.
13
+ - `host_cgroup_memory_limit` carries `defer` as a bool (`True` when both caps are finite and plausible, including `MemorySwapMax=0`).
14
+ - `round_cgroup_memory` may carry those PSI fields as optional flat keys at emit time; omitted when unread.
15
+
16
+ ### Docs
17
+ - SECURITY.md: isolation is the operator's job (`exec_prefix`, dedicated user, no passwordless sudo, egress); agent-runner stays lifecycle-safety.
18
+
19
+ No config migration required. `peek --json` schema stays `2.6`.
20
+
21
+ ## [0.3.12] - 2026-09-18
22
+
23
+ ### Added
24
+ - `[goal]` config table: a markdown lessons `ledger` (listed in `[prompt] files` at index >= 1, and — since it must survive the round's own `git stash` — required to sit under `log_dir` or outside `work_dir`) plus `[[goal.checks]]` (`name`, `cmd` argv, optional `timeout_s`) — objective, CLI-agnostic checks the round child runs after each round's agent exits, each emitting a `goal_check` event (`satisfied` bool, optional `value`).
25
+ - Treadmill assessor (advisory-only): across the last 3 completed rounds, if every round showed activity but at least one goal check stayed unsatisfied with an unchanged signature, serve writes one edge-triggered advisory to the ledger and emits a `goal_assessment` event (never on convergence). The ledger's `[prompt] files` membership carries it into the next round's prompt.
26
+ - Advisory/kill firewall: the goal loop never CAUSES a kill or give-up — the kill/give-up path reads events by kind and stays blind to `goal_check`/`goal_assessment`. A goal check's own wall time does count toward the round's duration, so a long check can DELAY (never trigger) the fast-spin `stalled_no_progress`/`crash_loop` breakers; config load warns if the total check budget reaches that window.
27
+
28
+ Check timeouts fold into the round's own timeout budget. No config migration required (v0.3.11 silently ignores an unrecognized `[goal]` table). `peek --json` schema stays `2.6`.
29
+
8
30
  ## [0.3.11] - 2026-09-17
9
31
 
10
32
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cli-agent-runner
3
- Version: 0.3.11
3
+ Version: 0.3.13
4
4
  Summary: Restart-on-exit supervisor for autonomous CLI agents
5
5
  Project-URL: Homepage, https://github.com/wan9yu/cli-agent-runner
6
6
  Project-URL: Documentation, https://github.com/wan9yu/cli-agent-runner#readme
@@ -37,6 +37,12 @@ unprivileged user, inside a container or VM, without passwordless sudo, and
37
37
  with egress limits. The supervisor's defenses bound the blast radius of a
38
38
  *misbehaving* agent, not a *hostile* one.
39
39
 
40
+ Isolation is the operator's job. Isolation-enforcing supervisors exist;
41
+ agent-runner deliberately is not one of them. Lifecycle-safety stays ours
42
+ (round timeouts, process-group reaping, orphan stashing, auto-stop on
43
+ critical alerts). Containment stays yours: `[agent] exec_prefix`, a dedicated
44
+ unprivileged user, no passwordless sudo, and egress limits.
45
+
40
46
  ## Containment
41
47
 
42
48
  If you need to bound what the agent can reach, run it inside a container (or
@@ -0,0 +1,135 @@
1
+ """Sanctioned timeout-bounded subprocess primitive.
2
+
3
+ Runs a command in its OWN session under a wall-clock timeout, escalating
4
+ TERM -> grace -> killpg(SIGKILL) on breach so a hung process leaves no
5
+ descendants (a backgrounded grandchild included, since killpg targets the
6
+ whole process group). Returns a ``BoundedResult`` with ``timed_out=True`` on
7
+ breach instead of raising -- the caller decides how to react. No ``clock``
8
+ parameter: ``subprocess.communicate(timeout=)`` self-times, there is no raw
9
+ wall-clock read here to abstract.
10
+
11
+ Two hardening properties beyond the plain TERM/KILL escalation:
12
+
13
+ - **Bounded even against a session-detached pipe holder.** A descendant that
14
+ ``setsid``-detaches out of the killed process group can still hold the
15
+ inherited stdout/stderr pipe open, so the final drain after ``killpg`` would
16
+ block forever waiting on EOF. Every drain is capped at ``_KILL_GRACE_S``; on
17
+ breach the read ends are closed and whatever was captured so far is returned.
18
+ The whole primitive therefore returns within ``timeout_s + 2*_KILL_GRACE_S``
19
+ (one grace for TERM->KILL, one for the post-kill drain) no matter what the
20
+ command spawns. A truly detached grandchild in its OWN session is NOT reaped
21
+ -- killpg cannot reach it; keep checks in a single process group.
22
+ - **Reap-on-interrupt.** A SIGTERM to the supervisor (see
23
+ ``cli.common.install_term_handler``, which raises ``KeyboardInterrupt``)
24
+ propagates out of ``communicate()`` while the command runs in its own
25
+ session and would otherwise never be signalled. Any ``BaseException`` out of
26
+ a wait therefore SIGKILLs the whole group, closes the pipes, and reaps under
27
+ one bounded ``_KILL_GRACE_S`` wait before re-raising, mirroring
28
+ ``agent_runtime._kill_pgroup``'s bounded-wait-after-SIGKILL.
29
+
30
+ Kept out of the startup import graph on purpose (see
31
+ tests/invariants/test_import_footprint.py): callers import it function-scope.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import os
37
+ import signal
38
+ import subprocess # noqa: TID251 — agent_runner._bounded is the sanctioned timeout-bounded subprocess primitive
39
+ from dataclasses import dataclass
40
+ from pathlib import Path
41
+
42
+ _KILL_GRACE_S = 3 # grace between TERM and killpg(SIGKILL), and for the post-kill drain
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class BoundedResult:
47
+ rc: int
48
+ stdout: str
49
+ stderr: str
50
+ timed_out: bool
51
+
52
+
53
+ def _kill_group(proc: subprocess.Popen) -> None:
54
+ try:
55
+ os.killpg(proc.pid, signal.SIGKILL)
56
+ except OSError:
57
+ pass
58
+
59
+
60
+ def _close_pipes(proc: subprocess.Popen) -> None:
61
+ for pipe in (proc.stdout, proc.stderr):
62
+ if pipe is not None:
63
+ try:
64
+ pipe.close()
65
+ except OSError:
66
+ pass
67
+
68
+
69
+ def _drain_after_kill(proc: subprocess.Popen, exc: subprocess.TimeoutExpired) -> tuple[str, str]:
70
+ """Best-effort drain after ``killpg(SIGKILL)`` that never blocks unbounded.
71
+
72
+ A ``setsid``-detached descendant can still hold the stdout/stderr pipe open,
73
+ so stop waiting on it: close the read ends and reap the (already-SIGKILLed)
74
+ immediate child under one more ``_KILL_GRACE_S`` cap. ``TimeoutExpired.output``
75
+ is ``bytes`` even on this text-mode Popen (the partial read captured at
76
+ timeout bypasses the text-decoding wrapper), so decode it here with the
77
+ same ``errors="replace"`` policy; fall back to empty strings if the
78
+ attribute isn't bytes at all."""
79
+ _close_pipes(proc)
80
+ try:
81
+ proc.wait(timeout=_KILL_GRACE_S)
82
+ except subprocess.TimeoutExpired:
83
+ pass
84
+ out = (
85
+ exc.output.decode("utf-8", errors="replace")
86
+ if isinstance(exc.output, (bytes, bytearray))
87
+ else ""
88
+ )
89
+ err = (
90
+ exc.stderr.decode("utf-8", errors="replace")
91
+ if isinstance(exc.stderr, (bytes, bytearray))
92
+ else ""
93
+ )
94
+ return out, err
95
+
96
+
97
+ def run_bounded(argv: list[str], *, cwd: Path, timeout_s: int) -> BoundedResult:
98
+ """Run argv in its own session under a wall-clock timeout, escalating
99
+ TERM -> grace -> killpg on breach so a hung command leaves no reachable
100
+ descendants. Returns timed_out=True on breach -- never raises on breach.
101
+ A ``BaseException`` out of a wait (e.g. a SIGTERM-driven KeyboardInterrupt)
102
+ SIGKILLs the group, closes the pipes, and reaps under one bounded
103
+ ``_KILL_GRACE_S`` wait, then re-raises, so an interrupted check is torn
104
+ down (not left a zombie) rather than orphaned. ``errors="replace"`` keeps a
105
+ non-UTF-8 byte in the command's output from raising a UnicodeDecodeError
106
+ past the caller's fail-open guard."""
107
+ proc = subprocess.Popen(
108
+ argv,
109
+ cwd=cwd,
110
+ stdout=subprocess.PIPE,
111
+ stderr=subprocess.PIPE,
112
+ text=True,
113
+ errors="replace",
114
+ start_new_session=True,
115
+ )
116
+ try:
117
+ try:
118
+ out, err = proc.communicate(timeout=timeout_s)
119
+ return BoundedResult(rc=proc.returncode, stdout=out, stderr=err, timed_out=False)
120
+ except subprocess.TimeoutExpired:
121
+ proc.terminate()
122
+ try:
123
+ out, err = proc.communicate(timeout=_KILL_GRACE_S)
124
+ except subprocess.TimeoutExpired as exc:
125
+ _kill_group(proc)
126
+ out, err = _drain_after_kill(proc, exc)
127
+ return BoundedResult(rc=proc.returncode, stdout=out, stderr=err, timed_out=True)
128
+ except BaseException:
129
+ _kill_group(proc)
130
+ _close_pipes(proc)
131
+ try:
132
+ proc.wait(timeout=_KILL_GRACE_S)
133
+ except subprocess.TimeoutExpired:
134
+ pass
135
+ raise
@@ -21,6 +21,7 @@ from agent_runner.cli.init_cmd import _preset_names
21
21
  from agent_runner.config import (
22
22
  AgentConfig,
23
23
  Config,
24
+ GoalConfig,
24
25
  MonitorConfig,
25
26
  MonitorHostHealthConfig,
26
27
  PhasesConfig,
@@ -29,6 +30,7 @@ from agent_runner.config import (
29
30
  RuntimeConfig,
30
31
  ScheduleConfig,
31
32
  VcsConfig,
33
+ _GoalCheckConfig,
32
34
  _HostHealthBrakeConfig,
33
35
  _HostHealthDiskConfig,
34
36
  _HostHealthMemoryConfig,
@@ -72,6 +74,7 @@ _SECTIONS: list[tuple[str, type, list[tuple[str, type]]]] = [
72
74
  ("phases", PhasesConfig, []),
73
75
  ("plugins", PluginsConfig, []),
74
76
  ("schedule", ScheduleConfig, []),
77
+ ("goal", GoalConfig, [("goal.checks", _GoalCheckConfig)]),
75
78
  ]
76
79
 
77
80
 
@@ -102,13 +105,23 @@ def _field_table(dc: type) -> list[str]:
102
105
  return rows
103
106
 
104
107
 
108
+ # Nested sub-sections that are TOML arrays-of-tables (`[[name]]`), not single
109
+ # sub-tables (`[name]`): the parent field is a list/tuple of the sub-dataclass,
110
+ # so the header must double-bracket or a copy-pasted single `[goal.checks]` is
111
+ # rejected by the parser (_parse_goal expects a list).
112
+ _ARRAY_OF_TABLES_SECTIONS = frozenset({"goal.checks"})
113
+
114
+
105
115
  def render_config_schema_table() -> str:
106
116
  """Markdown sub-sections per Config dataclass with field/type/default."""
107
117
  parts: list[str] = []
108
118
  for name, dc, nested in _SECTIONS:
109
119
  parts += [f"### `[{name}]`", "", *_field_table(dc), ""]
110
120
  for sub_name, sub_dc in nested:
111
- parts += [f"#### `[{sub_name}]`", "", *_field_table(sub_dc), ""]
121
+ brackets = (
122
+ f"[[{sub_name}]]" if sub_name in _ARRAY_OF_TABLES_SECTIONS else f"[{sub_name}]"
123
+ )
124
+ parts += [f"#### `{brackets}`", "", *_field_table(sub_dc), ""]
112
125
  return "\n".join(parts).rstrip()
113
126
 
114
127
 
@@ -112,6 +112,7 @@ def emit_host_cgroup_memory_limit(
112
112
  memory_max: int | None,
113
113
  memory_swap_max: int | None,
114
114
  cgroup_path: str | None,
115
+ defer: bool,
115
116
  swap_total_bytes: int | None = None,
116
117
  swap_cap_pct: float | None = None,
117
118
  memory_high: int | None = None,
@@ -120,8 +121,11 @@ def emit_host_cgroup_memory_limit(
120
121
  ) -> None:
121
122
  """Emit once at serve startup: this process's cgroup v2 memory budget
122
123
  (``metrics.cgroup_memory_limits``). ``None`` fields mean unlimited (or
123
- cgroup v2 unavailable). Serve uses this once to decide whether the
124
- mid-round hard floor can defer to kernel cgroup-OOM -- see
124
+ cgroup v2 unavailable). ``defer`` is the already-computed mid-round-floor
125
+ decision (True when both caps are finite and plausible, including
126
+ ``MemorySwapMax=0``) -- the event carries that yes/no, not just the
127
+ inputs. Serve uses this once to decide whether the mid-round hard floor
128
+ can defer to kernel cgroup-OOM -- see
125
129
  ``emit_mem_pressure_deferred_to_cgroup`` below.
126
130
 
127
131
  This also carries an optional startup advisory
@@ -152,6 +156,7 @@ def emit_host_cgroup_memory_limit(
152
156
  memory_max=memory_max,
153
157
  memory_swap_max=memory_swap_max,
154
158
  cgroup_path=cgroup_path,
159
+ defer=defer,
155
160
  swap_total_bytes=swap_total_bytes,
156
161
  swap_cap_pct=swap_cap_pct,
157
162
  memory_high=memory_high,
@@ -171,6 +176,9 @@ def emit_round_cgroup_memory(
171
176
  events_oom_delta: int,
172
177
  events_oom_kill_delta: int,
173
178
  bounding_cgroup_path: str | None,
179
+ io_psi_some_avg10: float | None = None,
180
+ io_psi_full_avg10: float | None = None,
181
+ psi_full_total: float | int | None = None,
174
182
  ) -> None:
175
183
  """Emit once per round: the round's peak cgroup ``memory.current`` (+swap) and
176
184
  the ``memory.events`` DELTAS (high/max/oom/oom_kill) over the round, read at the
@@ -185,9 +193,24 @@ def emit_round_cgroup_memory(
185
193
  process's own) cgroup; here it names the BOUNDING ANCESTOR (the one whose
186
194
  ``memory.max`` actually binds). Same key, two meanings across the two
187
195
  events an operator correlates would be a trap -- events.md pins field
188
- semantics permanently once shipped, so the distinct name is chosen now."""
196
+ semantics permanently once shipped, so the distinct name is chosen now.
197
+
198
+ Optional corroborating PSI fields (``io_psi_some_avg10``,
199
+ ``io_psi_full_avg10``, ``psi_full_total``) come from ``metrics.sample()``
200
+ at emit time. Each is omitted when unread (``None``). They ride FLAT on
201
+ this event -- never copied into ``Pressure.context``, so the kill ladder
202
+ stays blind to them."""
189
203
  from agent_runner.events import ROUND_CGROUP_MEMORY, emit
190
204
 
205
+ extra = {
206
+ k: v
207
+ for k, v in (
208
+ ("io_psi_some_avg10", io_psi_some_avg10),
209
+ ("io_psi_full_avg10", io_psi_full_avg10),
210
+ ("psi_full_total", psi_full_total),
211
+ )
212
+ if v is not None
213
+ }
191
214
  emit(
192
215
  log_dir,
193
216
  ROUND_CGROUP_MEMORY,
@@ -199,6 +222,7 @@ def emit_round_cgroup_memory(
199
222
  events_oom_delta=events_oom_delta,
200
223
  events_oom_kill_delta=events_oom_kill_delta,
201
224
  bounding_cgroup_path=bounding_cgroup_path,
225
+ **extra,
202
226
  )
203
227
 
204
228
 
@@ -56,7 +56,7 @@ def outer_round_ceiling_s(cfg: Config, phase_arg: str | None) -> int:
56
56
  (cfg.profile_for(p).runtime.round_budget_s for p in (cfg.phases.list or [])),
57
57
  default=cfg.runtime.round_budget_s,
58
58
  )
59
- _, outer_ceiling = timeout_budget(inner)
59
+ _, outer_ceiling = timeout_budget(inner, goal_checks_allowance_s=cfg.goal_checks_allowance_s)
60
60
  return outer_ceiling
61
61
 
62
62
 
@@ -303,17 +303,18 @@ _STOP_GRACE_MARGIN_S = 10 # pad above _ROUND_TERM_GRACE_S for systemd stop-requ
303
303
  _ROUND_UNREAPED_RC = 137 # 128 + SIGKILL(9): reads as a kill in the crash-loop path
304
304
 
305
305
 
306
- def timeout_budget(round_budget_s: int) -> tuple[int, int]:
306
+ def timeout_budget(round_budget_s: int, *, goal_checks_allowance_s: int = 0) -> tuple[int, int]:
307
307
  """Single source for the round-timeout safety budget.
308
308
 
309
309
  Returns ``(timeout_stop_sec, outer_ceiling_s)``:
310
310
 
311
311
  - ``outer_ceiling_s`` — the in-process outer wall-clock ceiling for the
312
312
  round subprocess (``api.outer_round_ceiling_s``): ``round_budget_s``
313
- plus reap grace + git-commit ceiling + hook allowance, so it only trips
314
- when the round supervisor itself is wedged, never during its own
315
- bounded post-round cleanup. The reap margin must cover the widest grace
316
- ANY round can be configured with -- a cooperative agent's
313
+ plus reap grace + git-commit ceiling + hook allowance + the goal-check
314
+ executor's own allowance, so it only trips when the round supervisor
315
+ itself is wedged, never during its own bounded post-round cleanup (nor
316
+ a slow-but-still-bounded goal check). The reap margin must cover the
317
+ widest grace ANY round can be configured with -- a cooperative agent's
317
318
  ``sigterm_grace_s`` is boot-capped at ``_ROUND_TERM_GRACE_S``
318
319
  (``config/models.py``'s mirror of this module's single source), not
319
320
  the non-cooperative default ``_REAP_GRACE_S``, so the margin is
@@ -324,12 +325,19 @@ def timeout_budget(round_budget_s: int) -> tuple[int, int]:
324
325
  SIGTERM to reach and drain the round (``_ROUND_TERM_GRACE_S``) plus a
325
326
  stop-request overhead pad, so `systemctl stop` never SIGKILLs a round
326
327
  that is draining normally.
328
+
329
+ ``goal_checks_allowance_s`` (default 0, byte-identical to the old
330
+ signature when omitted) is the caller's ``GoalConfig.checks_allowance_s``
331
+ when ``[goal]`` is configured -- folded into this ONE ceiling rather than
332
+ a second, separate one, so the goal-check path can never itself trip
333
+ ``round_supervisor_wedged``.
327
334
  """
328
335
  outer_ceiling_s = (
329
336
  round_budget_s
330
337
  + max(_REAP_GRACE_S, _ROUND_TERM_GRACE_S)
331
338
  + _GIT_COMMIT_TIMEOUT_S
332
339
  + _HOOK_ALLOWANCE_S
340
+ + goal_checks_allowance_s
333
341
  )
334
342
  timeout_stop_sec = outer_ceiling_s + _ROUND_TERM_GRACE_S + _STOP_GRACE_MARGIN_S
335
343
  return timeout_stop_sec, outer_ceiling_s
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.3.11'
22
- __version_tuple__ = version_tuple = (0, 3, 11)
21
+ __version__ = version = '0.3.13'
22
+ __version_tuple__ = version_tuple = (0, 3, 13)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -128,6 +128,8 @@ def _emit_round_cgroup_memory(
128
128
  def _delta(key: str) -> int:
129
129
  return max(0, cur_ev.get(key, 0) - base_ev.get(key, 0))
130
130
 
131
+ # One sample() at emit -- corroborating IO/mem-PSI, omitted when unread.
132
+ s = metrics.sample()
131
133
  emit_round_cgroup_memory(
132
134
  log_dir,
133
135
  round_num=round_num,
@@ -138,6 +140,9 @@ def _emit_round_cgroup_memory(
138
140
  events_oom_delta=_delta("oom"),
139
141
  events_oom_kill_delta=_delta("oom_kill"),
140
142
  bounding_cgroup_path=state["bounding_cgroup_path"],
143
+ io_psi_some_avg10=s.get("io_psi_some_avg10"),
144
+ io_psi_full_avg10=s.get("io_psi_full_avg10"),
145
+ psi_full_total=s.get("psi_full_total"),
141
146
  )
142
147
  return cur, oom_kill_baseline
143
148
 
@@ -277,8 +282,9 @@ def _release_brake(log_dir: Path, round_num: int, previous: str, *, reason: str)
277
282
 
278
283
  def _probe_and_emit_cgroup_defer(log_dir: Path, *, brake_memory_high: bool = False) -> bool:
279
284
  """Probe this process's cgroup v2 memory budget once at serve startup,
280
- emit host_cgroup_memory_limit for observability, and return whether the
281
- mid-round hard floor should defer to kernel cgroup-OOM: True only when
285
+ emit host_cgroup_memory_limit (including the ``defer`` decision, not just
286
+ the inputs) for observability, and return whether the mid-round hard
287
+ floor should defer to kernel cgroup-OOM: True only when
282
288
  BOTH memory.max and memory.swap.max are finite (the field host's
283
289
  MemoryMax=320M + MemorySwapMax=160M shape) -- that budget is bounded end
284
290
  to end, so cgroup-OOM WILL fire and contain the agent while the host
@@ -401,6 +407,7 @@ def _probe_and_emit_cgroup_defer(log_dir: Path, *, brake_memory_high: bool = Fal
401
407
  memory_max=limits["memory_max"],
402
408
  memory_swap_max=swap_max,
403
409
  cgroup_path=limits["cgroup_path"],
410
+ defer=defer,
404
411
  swap_total_bytes=swap_total,
405
412
  swap_cap_pct=swap_cap_pct,
406
413
  memory_high=memory_high,
@@ -40,6 +40,7 @@ from agent_runner.config.models import ( # noqa: F401 — public re-export
40
40
  _VCS_ALLOWED_FIELDS,
41
41
  AgentConfig,
42
42
  Config,
43
+ GoalConfig,
43
44
  MonitorConfig,
44
45
  MonitorHostHealthConfig,
45
46
  PhaseOverride,
@@ -50,17 +51,22 @@ from agent_runner.config.models import ( # noqa: F401 — public re-export
50
51
  RuntimeConfig,
51
52
  ScheduleConfig,
52
53
  VcsConfig,
54
+ _GoalCheckConfig,
53
55
  _HostHealthBrakeConfig,
54
56
  _HostHealthDiskConfig,
55
57
  _HostHealthMemoryConfig,
56
58
  _HostHealthPressureConfig,
57
59
  )
58
- from agent_runner.config.validators import _reject_control_chars # noqa: F401
60
+ from agent_runner.config.validators import ( # noqa: F401 — public re-export
61
+ _reject_control_chars,
62
+ _resolve_against_work_dir,
63
+ )
59
64
 
60
65
  __all__ = [
61
66
  "AgentConfig",
62
67
  "Config",
63
68
  "ConfigError",
69
+ "GoalConfig",
64
70
  "MonitorConfig",
65
71
  "MonitorHostHealthConfig",
66
72
  "PhaseOverride",