delegate-agent-cli 0.31.0__tar.gz → 0.33.0__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 (361) hide show
  1. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/CHANGELOG.md +1205 -1
  2. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/CONTRIBUTING.md +7 -6
  3. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/PKG-INFO +35 -18
  4. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/README.md +32 -17
  5. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/bin/delegate-profile-shim +131 -4
  6. delegate_agent_cli-0.33.0/bin/delegate.py +31 -0
  7. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/config.example.json +21 -1
  8. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/agent-setup.md +5 -4
  9. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/cli-reference.md +686 -145
  10. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/configuration.md +327 -23
  11. delegate_agent_cli-0.33.0/docs/delegate-workflows.md +784 -0
  12. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/development.md +21 -12
  13. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/live-runtime.md +4 -2
  14. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/publishing-checklist.md +2 -8
  15. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/security-model.md +293 -14
  16. delegate_agent_cli-0.33.0/docs/troubleshooting.md +1040 -0
  17. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/worktrees.md +257 -5
  18. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/pyproject.toml +8 -1
  19. delegate_agent_cli-0.33.0/src/delegate_agent/__init__.py +24 -0
  20. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/argv_builders.py +186 -0
  21. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/argv_utils.py +1 -0
  22. delegate_agent_cli-0.33.0/src/delegate_agent/auth_health.py +295 -0
  23. delegate_agent_cli-0.33.0/src/delegate_agent/auto_resume.py +227 -0
  24. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/bundled_models.py +15 -10
  25. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/capability_commands.py +27 -3
  26. delegate_agent_cli-0.33.0/src/delegate_agent/child_failures.py +189 -0
  27. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/cli.py +428 -58
  28. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/cli_parser.py +455 -49
  29. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/command_help.py +606 -82
  30. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/config.py +180 -29
  31. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/constants.py +8 -3
  32. delegate_agent_cli-0.33.0/src/delegate_agent/degraded.py +450 -0
  33. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/describe_payload.py +227 -42
  34. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/errors.py +2 -0
  35. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/failover_state.py +10 -3
  36. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/followup_command.py +116 -8
  37. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/harness_discovery.py +25 -0
  38. delegate_agent_cli-0.33.0/src/delegate_agent/harness_enabled.py +120 -0
  39. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/harness_events.py +541 -46
  40. delegate_agent_cli-0.33.0/src/delegate_agent/inspection_commands.py +410 -0
  41. delegate_agent_cli-0.33.0/src/delegate_agent/lane_health.py +705 -0
  42. delegate_agent_cli-0.33.0/src/delegate_agent/lane_slice.py +327 -0
  43. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/mail.py +78 -2
  44. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/mail_core.py +151 -1
  45. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/mail_push.py +24 -2
  46. delegate_agent_cli-0.33.0/src/delegate_agent/model_discovery.py +770 -0
  47. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/notify.py +42 -5
  48. delegate_agent_cli-0.33.0/src/delegate_agent/outcome.py +279 -0
  49. delegate_agent_cli-0.33.0/src/delegate_agent/outside_cwd_changes.py +143 -0
  50. delegate_agent_cli-0.33.0/src/delegate_agent/pending_tool.py +80 -0
  51. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/private_io.py +13 -3
  52. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/profiles.py +67 -22
  53. delegate_agent_cli-0.33.0/src/delegate_agent/prompt_instructions.py +84 -0
  54. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/prompt_transport.py +13 -0
  55. delegate_agent_cli-0.33.0/src/delegate_agent/provider_errors.py +816 -0
  56. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/reasoning.py +38 -7
  57. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/record_io.py +28 -2
  58. delegate_agent_cli-0.33.0/src/delegate_agent/registry_roster.py +279 -0
  59. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/rendering.py +135 -4
  60. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/request_build.py +844 -90
  61. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/request_models.py +80 -4
  62. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/resume_command.py +91 -7
  63. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/retention.py +272 -6
  64. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/run_context.py +18 -1
  65. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/run_metadata.py +3 -1
  66. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/run_output_commands.py +55 -3
  67. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/run_registry.py +143 -19
  68. delegate_agent_cli-0.33.0/src/delegate_agent/run_scratch.py +554 -0
  69. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/run_status.py +217 -39
  70. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/runner.py +1886 -315
  71. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/safe_workspace.py +111 -3
  72. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/sandbox_bwrap.py +111 -1
  73. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/seatbelt.py +61 -2
  74. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/snapshot_view.py +32 -2
  75. delegate_agent_cli-0.33.0/src/delegate_agent/stall_watchdog.py +1288 -0
  76. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/stream_capture.py +106 -18
  77. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/structured_output.py +65 -1
  78. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/terminal_states.py +12 -1
  79. delegate_agent_cli-0.33.0/src/delegate_agent/usage_record.py +67 -0
  80. delegate_agent_cli-0.33.0/src/delegate_agent/wait_cancel_commands.py +1308 -0
  81. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflow_identity.py +14 -4
  82. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflow_pinning.py +362 -6
  83. delegate_agent_cli-0.33.0/src/delegate_agent/workflows/__init__.py +12 -0
  84. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflows/commands.py +709 -44
  85. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflows/registry.py +47 -6
  86. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflows/runtime.py +1920 -188
  87. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflows/schema.py +99 -4
  88. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflows/script.py +54 -3
  89. delegate_agent_cli-0.33.0/src/delegate_agent/workflows/stage_guard.py +92 -0
  90. delegate_agent_cli-0.33.0/src/delegate_agent/workflows/status_reasons.py +163 -0
  91. delegate_agent_cli-0.33.0/src/delegate_agent/workspace_spec.py +636 -0
  92. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_commands.py +44 -9
  93. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_execution.py +322 -12
  94. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_gc.py +367 -35
  95. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_mgmt.py +300 -36
  96. delegate_agent_cli-0.33.0/src/delegate_agent/worktree_procs.py +234 -0
  97. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_records.py +20 -1
  98. delegate_agent_cli-0.33.0/src/delegate_agent/worktree_remove.py +868 -0
  99. delegate_agent_cli-0.33.0/src/delegate_agent/worktree_salvage.py +224 -0
  100. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/worktree_summary.py +98 -32
  101. delegate_agent_cli-0.33.0/src/delegate_agent/write_guard.py +727 -0
  102. delegate_agent_cli-0.33.0/src/delegate_agent/write_guard_launch.py +383 -0
  103. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/PKG-INFO +35 -18
  104. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +76 -0
  105. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/requires.txt +2 -0
  106. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_acceptance_toolchain.py +58 -24
  107. delegate_agent_cli-0.33.0/tests/test_appendix_failure_strings.py +141 -0
  108. delegate_agent_cli-0.33.0/tests/test_auth_health.py +366 -0
  109. delegate_agent_cli-0.33.0/tests/test_auto_resume.py +457 -0
  110. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_bundled_models_lane_r.py +6 -19
  111. delegate_agent_cli-0.33.0/tests/test_c5_validation.py +502 -0
  112. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_cancel_terminal_override.py +179 -0
  113. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_capability_commands.py +60 -86
  114. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_child_failures.py +19 -12
  115. delegate_agent_cli-0.33.0/tests/test_cli_ergonomics.py +308 -0
  116. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_cli_simplification.py +11 -28
  117. delegate_agent_cli-0.33.0/tests/test_cli_surface_fixes.py +108 -0
  118. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_codex_pure_sandbox.py +68 -1
  119. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_codex_scratch.py +222 -20
  120. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_codex_thread_resilience.py +1 -1
  121. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_command_help.py +32 -85
  122. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_config_commands.py +179 -15
  123. delegate_agent_cli-0.33.0/tests/test_cross_registry_lookup.py +310 -0
  124. delegate_agent_cli-0.33.0/tests/test_degraded.py +585 -0
  125. delegate_agent_cli-0.33.0/tests/test_degraded_runs.py +787 -0
  126. delegate_agent_cli-0.33.0/tests/test_delegate_audit_shim.py +33 -0
  127. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_delegate_help_cli.py +64 -114
  128. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_delegate_isolation.py +25 -16
  129. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_delegate_parser.py +194 -274
  130. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_delegate_validation.py +128 -79
  131. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_describe_models_binary.py +57 -0
  132. delegate_agent_cli-0.33.0/tests/test_describe_workflow_dsl.py +93 -0
  133. delegate_agent_cli-0.33.0/tests/test_devin_bounded_output.py +181 -0
  134. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_devin_read_only_behavior.py +26 -14
  135. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_discovery_contracts.py +1 -1
  136. delegate_agent_cli-0.33.0/tests/test_docs_parser_conformance.py +497 -0
  137. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_end_to_end_tracking.py +25 -1
  138. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_engine_argv.py +70 -13
  139. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_execution_argv_and_prompt.py +657 -20
  140. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_execution_dry_run.py +29 -27
  141. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_execution_worktree_failure_cleanup.py +4 -68
  142. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_execution_worktree_preflight.py +24 -0
  143. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_execution_worktree_run.py +343 -15
  144. delegate_agent_cli-0.33.0/tests/test_failover_state.py +173 -0
  145. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_followup_after_retry_e2e.py +144 -0
  146. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_followup_capture.py +47 -2
  147. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_followup_parser.py +5 -11
  148. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_followup_refusals.py +87 -2
  149. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_harness_discovery.py +33 -49
  150. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_harness_events.py +292 -173
  151. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_inspection_commands.py +115 -60
  152. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_lane_a_transport.py +112 -73
  153. delegate_agent_cli-0.33.0/tests/test_lane_b_lifecycle.py +194 -0
  154. delegate_agent_cli-0.33.0/tests/test_lane_health.py +733 -0
  155. delegate_agent_cli-0.33.0/tests/test_lane_known_bad.py +274 -0
  156. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_lane_m_mail_defaults.py +1 -1
  157. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_lane_s_structured_retry.py +60 -3
  158. delegate_agent_cli-0.33.0/tests/test_lane_slice.py +477 -0
  159. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_launch_input_parity.py +50 -11
  160. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_launch_mapping.py +2 -4
  161. delegate_agent_cli-0.33.0/tests/test_launch_model_resolution.py +276 -0
  162. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_bounds.py +13 -13
  163. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_contract.py +23 -0
  164. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_delivery.py +25 -16
  165. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_eligibility.py +28 -7
  166. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_framing.py +2 -27
  167. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_gating.py +9 -11
  168. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_prune.py +0 -12
  169. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push.py +21 -61
  170. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push_adapters.py +14 -8
  171. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push_batch.py +4 -6
  172. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push_cursor.py +25 -5
  173. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push_gating.py +1 -1
  174. delegate_agent_cli-0.33.0/tests/test_mail_push_inheritance.py +120 -0
  175. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_push_seams.py +12 -3
  176. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_resolution.py +25 -17
  177. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_rules.py +7 -0
  178. delegate_agent_cli-0.33.0/tests/test_mail_unread_steering.py +267 -0
  179. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_model_discovery.py +206 -198
  180. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_model_selection_wave1a.py +24 -126
  181. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_model_selection_wave1b.py +13 -36
  182. delegate_agent_cli-0.33.0/tests/test_muse_engine.py +248 -0
  183. delegate_agent_cli-0.33.0/tests/test_no_page_ask.py +75 -0
  184. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_notify.py +72 -26
  185. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_omp_output_capture.py +48 -5
  186. delegate_agent_cli-0.33.0/tests/test_omp_provider_pin.py +1003 -0
  187. delegate_agent_cli-0.33.0/tests/test_omp_tool_update_compaction.py +243 -0
  188. delegate_agent_cli-0.33.0/tests/test_outcome_contract.py +718 -0
  189. delegate_agent_cli-0.33.0/tests/test_output_cap_off.py +236 -0
  190. delegate_agent_cli-0.33.0/tests/test_outside_cwd_changes.py +119 -0
  191. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_packaging.py +10 -3
  192. delegate_agent_cli-0.33.0/tests/test_partial_output_recovery.py +547 -0
  193. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_capability.py +9 -26
  194. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_claude.py +0 -11
  195. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_framer.py +44 -70
  196. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_opencode.py +0 -10
  197. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_parser.py +1 -13
  198. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_records.py +12 -13
  199. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_resolution.py +45 -113
  200. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_size_guard.py +12 -0
  201. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_workflow.py +104 -95
  202. delegate_agent_cli-0.33.0/tests/test_private_io_dir_race.py +57 -0
  203. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_proc_harness.py +23 -15
  204. delegate_agent_cli-0.33.0/tests/test_process_guard.py +149 -0
  205. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_profiles.py +108 -7
  206. delegate_agent_cli-0.33.0/tests/test_provider_error_docs.py +95 -0
  207. delegate_agent_cli-0.33.0/tests/test_provider_error_records.py +395 -0
  208. delegate_agent_cli-0.33.0/tests/test_provider_errors.py +265 -0
  209. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_provider_outcomes.py +12 -14
  210. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_pure_call.py +7 -2
  211. delegate_agent_cli-0.33.0/tests/test_python_version_guard.py +253 -0
  212. delegate_agent_cli-0.33.0/tests/test_real_binary_contracts.py +499 -0
  213. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_reasoning_capabilities.py +35 -169
  214. delegate_agent_cli-0.33.0/tests/test_registry_contention.py +279 -0
  215. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_registry_lock_guard.py +61 -0
  216. delegate_agent_cli-0.33.0/tests/test_registry_roster.py +183 -0
  217. delegate_agent_cli-0.33.0/tests/test_resumable_default.py +538 -0
  218. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_attachment.py +30 -7
  219. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_capture.py +6 -1
  220. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_inheritance.py +193 -4
  221. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_parser.py +4 -8
  222. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_trust.py +29 -10
  223. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_retention.py +85 -97
  224. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_run_listing_cost.py +7 -7
  225. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_run_registry.py +28 -71
  226. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_runner_capture.py +984 -84
  227. delegate_agent_cli-0.33.0/tests/test_runs_deadline.py +191 -0
  228. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_safe_workspace_isolation.py +161 -4
  229. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_sandbox_bwrap.py +211 -21
  230. delegate_agent_cli-0.33.0/tests/test_scratch_reclaim.py +943 -0
  231. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_simplification_integration.py +12 -4
  232. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_single_run_record.py +4 -0
  233. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_slash_passthrough.py +1 -0
  234. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_snapshot_redaction.py +10 -6
  235. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_snapshot_rendering.py +29 -0
  236. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_snapshot_run_output.py +260 -6
  237. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_snapshot_view.py +27 -0
  238. delegate_agent_cli-0.33.0/tests/test_stall_watchdog.py +1454 -0
  239. delegate_agent_cli-0.33.0/tests/test_stall_watchdog_runner.py +196 -0
  240. delegate_agent_cli-0.33.0/tests/test_status_truth.py +340 -0
  241. delegate_agent_cli-0.33.0/tests/test_stream_usage_ingest.py +205 -0
  242. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_structured_output.py +72 -0
  243. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_test_environment.py +2 -1
  244. delegate_agent_cli-0.33.0/tests/test_test_runner.py +83 -0
  245. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_tracked_output_bounds.py +27 -11
  246. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_tracked_timeout.py +0 -51
  247. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_utility_modules.py +7 -16
  248. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_wait_cancel_commands.py +543 -101
  249. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_wave4_launch_features.py +162 -8
  250. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_attempts.py +10 -5
  251. delegate_agent_cli-0.33.0/tests/test_workflow_check_keying.py +121 -0
  252. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_commands.py +606 -115
  253. delegate_agent_cli-0.33.0/tests/test_workflow_failure_policy.py +233 -0
  254. delegate_agent_cli-0.33.0/tests/test_workflow_key_stability.py +68 -0
  255. delegate_agent_cli-0.33.0/tests/test_workflow_keyed_steps.py +1125 -0
  256. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_pinning.py +6 -22
  257. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_profile_identity.py +5 -0
  258. delegate_agent_cli-0.33.0/tests/test_workflow_provider_stage.py +341 -0
  259. delegate_agent_cli-0.33.0/tests/test_workflow_repin.py +1264 -0
  260. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_retry_outcomes.py +598 -25
  261. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_schema.py +13 -2
  262. delegate_agent_cli-0.33.0/tests/test_workflow_session_fallback.py +413 -0
  263. delegate_agent_cli-0.33.0/tests/test_workflow_status_reasons.py +491 -0
  264. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_watchdog.py +41 -5
  265. delegate_agent_cli-0.33.0/tests/test_workspace_spec.py +1489 -0
  266. delegate_agent_cli-0.33.0/tests/test_worktree_effective_dirt.py +502 -0
  267. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_list_show.py +1 -24
  268. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_prune_gc.py +92 -33
  269. delegate_agent_cli-0.33.0/tests/test_worktree_reap_orphan.py +722 -0
  270. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_remove.py +419 -18
  271. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_retirement.py +175 -8
  272. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_safety_policy.py +11 -3
  273. delegate_agent_cli-0.33.0/tests/test_worktree_salvage.py +416 -0
  274. delegate_agent_cli-0.33.0/tests/test_worktree_symlink_placeholders.py +191 -0
  275. delegate_agent_cli-0.33.0/tests/test_write_guard.py +1543 -0
  276. delegate_agent_cli-0.33.0/tests/test_write_guard_e2e.py +333 -0
  277. delegate_agent_cli-0.33.0/tests/test_write_guard_live.py +499 -0
  278. delegate_agent_cli-0.31.0/bin/delegate.py +0 -15
  279. delegate_agent_cli-0.31.0/docs/delegate-workflows.md +0 -391
  280. delegate_agent_cli-0.31.0/docs/troubleshooting.md +0 -595
  281. delegate_agent_cli-0.31.0/src/delegate_agent/__init__.py +0 -5
  282. delegate_agent_cli-0.31.0/src/delegate_agent/child_failures.py +0 -133
  283. delegate_agent_cli-0.31.0/src/delegate_agent/inspection_commands.py +0 -190
  284. delegate_agent_cli-0.31.0/src/delegate_agent/model_discovery.py +0 -350
  285. delegate_agent_cli-0.31.0/src/delegate_agent/prompt_instructions.py +0 -58
  286. delegate_agent_cli-0.31.0/src/delegate_agent/run_scratch.py +0 -229
  287. delegate_agent_cli-0.31.0/src/delegate_agent/stall_watchdog.py +0 -607
  288. delegate_agent_cli-0.31.0/src/delegate_agent/wait_cancel_commands.py +0 -709
  289. delegate_agent_cli-0.31.0/src/delegate_agent/workflows/__init__.py +0 -6
  290. delegate_agent_cli-0.31.0/src/delegate_agent/worktree_remove.py +0 -465
  291. delegate_agent_cli-0.31.0/tests/test_failover_state.py +0 -105
  292. delegate_agent_cli-0.31.0/tests/test_lane_b_lifecycle.py +0 -73
  293. delegate_agent_cli-0.31.0/tests/test_process_guard.py +0 -42
  294. delegate_agent_cli-0.31.0/tests/test_registry_contention.py +0 -145
  295. delegate_agent_cli-0.31.0/tests/test_stall_watchdog.py +0 -445
  296. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/LICENSE +0 -0
  297. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/MANIFEST.in +0 -0
  298. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/SECURITY.md +0 -0
  299. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/docs/assets/delegate-agent-header.png +0 -0
  300. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.claude.json +0 -0
  301. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.codex.json +0 -0
  302. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.cursor.json +0 -0
  303. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.droid.json +0 -0
  304. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.grok.json +0 -0
  305. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.judge.json +0 -0
  306. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/examples/task.kimi.json +0 -0
  307. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/setup.cfg +0 -0
  308. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/account_binding.py +0 -0
  309. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/archived_logs.py +0 -0
  310. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/command_errors.py +0 -0
  311. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/config_commands.py +0 -0
  312. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/git_utils.py +0 -0
  313. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/isolation.py +0 -0
  314. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/json_types.py +0 -0
  315. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/log_output.py +0 -0
  316. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/personas.py +0 -0
  317. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/profile_commands.py +0 -0
  318. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/profile_guard.py +0 -0
  319. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/redaction.py +0 -0
  320. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/setup_commands.py +0 -0
  321. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/workflow_attempts.py +0 -0
  322. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent/wsl.py +0 -0
  323. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
  324. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
  325. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
  326. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_account_binding.py +0 -0
  327. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_call_capture_contract.py +0 -0
  328. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_call_text_fixtures.py +0 -0
  329. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_child_root_env.py +0 -0
  330. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_engine_universe.py +0 -0
  331. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_harness_json_limits.py +0 -0
  332. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_isolation_plans.py +0 -0
  333. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_kimi_commands.py +0 -0
  334. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_lane_r_public_surface.py +0 -0
  335. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_launch_error_hints.py +0 -0
  336. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_identity.py +0 -0
  337. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_io.py +0 -0
  338. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_parser.py +0 -0
  339. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_reply.py +0 -0
  340. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_mail_tempdir.py +0 -0
  341. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_nested_workflow_threads.py +0 -0
  342. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_omp_read_only_behavior.py +0 -0
  343. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_contract.py +0 -0
  344. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_persona_validation.py +0 -0
  345. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_private_directory_publish.py +0 -0
  346. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_private_io_bounded.py +0 -0
  347. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_projections.py +0 -0
  348. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_refusals.py +0 -0
  349. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_size_guard.py +0 -0
  350. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_resume_stale.py +0 -0
  351. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_run_record_imports.py +0 -0
  352. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_setup_commands.py +0 -0
  353. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_stall_watchdog_cursor.py +0 -0
  354. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_terminal_states.py +0 -0
  355. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_gate_binding.py +0 -0
  356. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_journal_reader.py +0 -0
  357. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_jsonl_cli.py +0 -0
  358. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_simplification.py +0 -0
  359. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_workflow_watchdog_unit.py +0 -0
  360. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_worktree_record_bundle.py +0 -0
  361. {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.33.0}/tests/test_wsl_guardrails.py +0 -0
@@ -5,6 +5,1208 @@ All notable changes to this project are 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
+ ## [Unreleased]
9
+
10
+ ## [0.33.0] - 2026-10-12
11
+
12
+ This release adds Muse (Meta's Muse family) as the eleventh harness, at parity with the best-supported engines: safe/work/call modes, structured output, native session followup, and the full effort enum.
13
+
14
+ ### Added
15
+ - **Muse is a Delegate harness.** `delegate muse safe|work|call` wraps
16
+ Meta's Muse (`muse exec --json`) with Muse family models: prompt-file
17
+ delivery, a flag-enforced safe lockdown (`--disable-write --disable-shell
18
+ --disable-web-tools`, sandbox on, workspace untrusted), work mode with the
19
+ sandbox kept on, native `--output-schema` enforcement, session followup and
20
+ auto-resume, and the full static effort enum through `ultra`. The `muse`
21
+ binary accepts unknown model IDs silently, so Delegate records no
22
+ served-model claim for Muse runs.
23
+
24
+ ### Fixed
25
+ - The test runner (`scripts/test.py`) no longer aborts on hosts where
26
+ lowering its own priority with `os.nice` is not permitted (sandboxed
27
+ agent harnesses); it warns and continues under the worker caps.
28
+
29
+ ## [0.32.0] - 2026-10-01
30
+
31
+ This release adds controls for launching and managing agents and resuming
32
+ workflows: `--force-launch`, `--writable`, `--kill-live`, and `--repin`.
33
+ It also improves progress reports, coordinator mail, failure recovery, and
34
+ write protection, and makes optional local profile tooling work on other
35
+ machines without requiring the maintainer's setup.
36
+
37
+ ### Added
38
+ - **Every work and safe Run carries the lane test rule.** After the operator's
39
+ task, the framed prompt adds a "Delegate lane test rule" section: lanes run
40
+ targeted tests only (the files they changed and their neighbours, through
41
+ `testrun` with worker caps), and the full suite and the project gate stay
42
+ with the coordinator (Trey, 2026-09-30). Call mode, unmoded prompts, and
43
+ slash passthrough are untouched; reframing never repeats it.
44
+ - `runs` and `ps` show how long a running Run has left: a running Run with a
45
+ timeout carries `deadlineAt` (recorded by the runner when its timeout clock
46
+ starts, after workspace setup) and `remainingSeconds`, and the text listing
47
+ prefixes `current` with `12m left`. Terminal Runs carry neither.
48
+ - **A live Run shows the tool call it is waiting on.** While a tool call is in
49
+ flight, `runs` and `snapshot` carry `pendingTool` (`name`, optional
50
+ `target`, `startedAt`, `seconds`; the oldest pending call, redacted like
51
+ `current`), and after a minute `current` reads `waiting on tool <name> for
52
+ 13m`. It is refreshed whenever the pending set changes, tracked even with
53
+ `--stall-minutes 0`, and dropped from terminal records. The stall watchdog
54
+ still never fires while a tool is pending: a hung tool is surfaced, not
55
+ killed.
56
+ - A followup or session resume whose child exits non-zero within 15 seconds
57
+ with no stdout or stderr stays `child_failed`, but the message says the
58
+ saved native session probably could not be loaded and `nextActions` offers
59
+ `delegate resume <handle>`.
60
+ - **Early-stop detection.** A succeeded Run whose last sentence announces a
61
+ next step ("Now let me write my report.") carries `degraded:
62
+ ended_announcing_next_step`, and a work Run that parks on an approval
63
+ question with no changed files carries `degraded: ended_awaiting_input`.
64
+ Status stays `succeeded`; the warning names `delegate resume <handle>`. A
65
+ finished deliverable that ends with "Should I proceed?" is not flagged.
66
+ - **Unread coordinator mail is surfaced at run end.** A terminal work Run
67
+ records `unreadMail` (count, up to three id/sender/subject rows, and an
68
+ `unreadable` count for damaged or non-regular inbox entries) plus a warning
69
+ on the state, envelope, completion report, snapshot, `wait`, and `cancel`
70
+ reply. Messages a push hook already injected count as read. The mail prompt
71
+ suffix now asks lanes to read their inbox before final verification or
72
+ commit. Launch, dry-run, and manifest carry `mailInbox` (host and mail
73
+ root).
74
+ - `followup` and `resume` inherit `--mail-push` from the source Run and accept
75
+ `--no-mail-push`; `followup` also accepts `--mail-push`.
76
+ - **One automatic fresh rerun for transient safe-mode failures.** A `safe`
77
+ Run that fails with `stream_disconnected` or `provider_unavailable`, or a
78
+ `safe` launch the broker refuses (`broker_binding_inactive`) with no child
79
+ output, is rerun once as a new Run recorded under `autoResume` with `kind:
80
+ "rerun"` and the first attempt's error. The first broker refusal defers the
81
+ known-bad lane marker (`laneMarkerDeferred: "broker_binding_retry"`). Work
82
+ Runs are never rerun this way. `providerErrors.autoResume: false` turns it
83
+ off.
84
+ - **`<engine>.enabled: false`** hides a harness from `models`, `describe`,
85
+ `capabilities`, and `capabilities refresh`, and any launch of it fails with
86
+ `harness_disabled` naming the key.
87
+ - **Claude model typos are refused before launch.** A typed `--model` that is
88
+ a family word plus a version (`opus-5.5`, `Sonnet 5`) or names no model
89
+ (`claude-`, an empty `[]`) fails with `invalid_alias` and the closest valid
90
+ values, on the CLI, dry runs, and `run --input-json`. Provider names
91
+ (Bedrock ARNs, Foundry deployments, gateway strings) still launch, and the
92
+ version-typo refusal is skipped when a Bedrock, Vertex, Foundry, or
93
+ `ANTHROPIC_BASE_URL` gateway variable is set.
94
+ - Workflow status says why and what next: a paused workflow carries a `pause`
95
+ object (gate, actions, last failure, rejection text) or a `softPark` reason
96
+ naming `delegate workflow resume <wfId>`; agent journal rows always carry
97
+ label and item, `agent_started` records `timeout` and `deadlineAt`,
98
+ `agent_timeout` names `nextEngine`, and status lists recent timeouts.
99
+ - Codex structured output: a root-array answer wrapped in a one-key object is
100
+ unwrapped on the prompt path and journaled as `agent_output_unwrapped`.
101
+ - Worktree warnings name the files: `dirty_source_auto_included_paths`,
102
+ `dirty_source_included`/`dirty_source_included_paths` for explicit
103
+ `--include-dirty`, `dirty_source_mirrored` for safe worktree launches, and
104
+ `dirtySourcePreview` in dry runs (up to five paths each).
105
+ - `workflow resume` and `workflow status` say when a workflow's pinned runtime
106
+ differs from the live one. The notice names both runtimes (digest, delegate
107
+ version, and the pin or promotion date), and JSON carries the same facts as
108
+ `runtimePin` (`differs`, `pinned`, `live`; on a resume the notice text is
109
+ also in `warnings`). `status` prints no notice for a finished (`succeeded`
110
+ or `dry_run`) workflow, and a pin that cannot be read reports `checked:
111
+ false` with the reason instead of blocking the resume.
112
+ - `--repin` on `workflow resume`, `workflow run --resume`, and `workflow
113
+ approve` is an opt-in that moves a workflow onto the live runtime; a plain
114
+ resume still keeps the pin. It keeps the journal, step keys, frozen script,
115
+ arguments, config, personas, and profile identity, moves the old runtime
116
+ into `runtimeHistory`, and journals one `runtime_repinned` event. The
117
+ identity check still runs first, and the new pin is written aside and
118
+ validated before it replaces the old one. A failed validation, a failed
119
+ launch, or a resume where no supervisor ever starts on the new pin restores
120
+ the old pin byte for byte and journals `runtime_repin_rolled_back`.
121
+ `--repin` is refused with `repin_children_running` while a child run is
122
+ running or still starting, with `invalid_option_combination` on a new run or
123
+ with `--dry-run`, with `workflow_locked` when another resume holds the lock,
124
+ and when the existing pin cannot be verified. `WORKFLOW_KEY_VERSION` remains
125
+ the guard against step-key drift. A script that branches on `capabilities`
126
+ can take a different branch after a repin, because the map then describes
127
+ the live runtime.
128
+ - `workflow check` warns about unkeyed steps in a script that keys others.
129
+ When some `agent()`, `parallel()`, `pipeline()`, or `workflow()` calls pass
130
+ `key=` and others do not, it emits one `keying warning` per primitive kind
131
+ with the source lines of the unkeyed calls (at most eight), because those
132
+ calls still replay by position and prompt and lose settled work when a
133
+ resumed script shifts. A script that keys nothing is left alone, a call with
134
+ a `**kwargs` expansion is assumed to carry its key, and `judges()` and
135
+ `followup()` are never flagged.
136
+ - A run or workflow id that is not in the current workspace is looked up in a
137
+ small roster of the other workspaces Delegate has launched in
138
+ (`~/.delegate/registries.json`, private, at most 256 entries, most recently
139
+ used first; workspaces whose `.delegate` is gone are skipped). `snapshot`
140
+ and `run-output` read a run id found in exactly one other workspace directly
141
+ and report `resolutionKind: "cross_registry"`, `resolvedWorkspace`, and a
142
+ `cross_registry:` warning. `wait`, `cancel`, `resume`, `followup`, and
143
+ `worktree show` never act across workspaces: they fail with
144
+ `unknown_handle`, name the workspace, and put the exact `delegate --cwd
145
+ <workspace> ...` command first in `nextActions`. The workflow read actions
146
+ `status`, `events`, `watch`, `wait`, and `result` read a workflow found in
147
+ exactly one other workspace, and `status`, `wait`, and `result` add
148
+ `resolvedWorkspace`; `approve`, `reject`, `kill`, and `run --resume` fail
149
+ with `workflow_not_found` and the exact command instead. An id found in
150
+ several workspaces is listed, never guessed, and a numbered alias is never
151
+ auto-resolved: the error lists every workspace that has it. Only workspaces
152
+ used for a launch after the roster shipped are known.
153
+ - OMP `tool_execution_update` records whose `args` or `partialResult` is
154
+ oversized are shrunk to a `{"delegateCompacted": true, "originalBytes": N,
155
+ "head": ..., "tail": ...}` stub. The sub-agent `task` tool re-emits its
156
+ whole shared context on every update; `tool_execution_start` and
157
+ `tool_execution_end` carry the full arguments and result once and are never
158
+ touched. `stdoutCapture` (policy `omp-capture-v2`) reports
159
+ `compactedToolUpdateRecords` and `compactedToolUpdateBytes`, the run gets a
160
+ warning, and `--raw` cannot recover the compacted text.
161
+ - Tracked Pi, OMP, and OpenCode runs ingest token `usage` from the stream,
162
+ summed per message or step and not double counted.
163
+ - Run and call envelopes, run summaries, and snapshots carry `servedModel`
164
+ and, when the harness reports one, `servedProvider`: what the child's stream
165
+ says actually answered. They sit beside `modelResolved` (the request),
166
+ repeat inside `modelProvenance`, and are absent when the harness reported no
167
+ model. A Claude call reports only a per-run `modelUsage` total, so its
168
+ `servedModel` is set only when exactly one model produced output; with
169
+ several it is absent and a pinned call warns `pinned_continuity_unverified`.
170
+ An ungrouped `call` is now held to the same continuity rule as a tracked
171
+ run: a pinned call whose stream names another model or provider fails with
172
+ `model_continuity_paused` (text mode prints no answer text for it; JSON
173
+ keeps any text beside the error), and a fungible or panel call carries the
174
+ `model_substitution` warning, provider included.
175
+ - A tracked run whose child ends its turn mid-job is flagged as degraded. A
176
+ headless child ends when its model stops, and background Bash tasks and
177
+ Monitors die with the session, so a Claude work run that said "Waiting on
178
+ the full gate" used to be recorded `succeeded` with `ok: true` while its
179
+ gate never finished. The status stays `succeeded` so the work can be
180
+ adopted, but the run now carries `degraded: true` (present only when true),
181
+ a `degradedReason`, `degradedEvidence` (bounded, redacted lines saying what
182
+ was seen), and a `degraded=...` warning. They appear on the launch envelope,
183
+ in state.json, on `wait` (JSON, `--structural`, and a `degraded:` line in
184
+ text), `snapshot`, `runs` and `ps` (the text table prefixes `current` with
185
+ `[degraded]`; `--structural` omits them), the `run-output
186
+ --completion-report` view, and, for a workflow child, the `agent_child`
187
+ journal event and `agent_meta()` (`degraded`, `degradedReason`). Nothing
188
+ fails a run or a workflow step, and `wait` still exits 0. `degradedReason`
189
+ is `ended_waiting_on_background_work` or
190
+ `background_work_unfinished_at_exit`. The first is a final-message check
191
+ that works for every engine: a short message that is not shaped like a
192
+ finished report and says the child is waiting on, or will act after,
193
+ unfinished work of its own. It ignores waiting on the requester ("I'll
194
+ commit when you approve"), a third party's independent result ("Waiting for
195
+ CI to post its result"), denials, and quoted text such as a tool's output; a
196
+ short report-shaped message matches only an explicit present-tense statement
197
+ that the child's own job is still running; runs with `--output-schema` are
198
+ not text-checked. The second reads Claude Code's `background_tasks_changed`
199
+ snapshot at the result event and catches a finished-looking report that
200
+ abandoned a running task; only Claude emits that event.
201
+ - A provider failure is now data. The failed run's envelope, run state, and
202
+ completion report carry a structured `providerError` (`status`,
203
+ `providerCode`, `message`, `engine`, `signature`, `class`, `hint`); the
204
+ message is bounded, credentials are redacted, and email addresses are
205
+ masked. `class` is `persistent`, `transient`, or `unknown`, and a persistent
206
+ signature is either lane-scoped (credentials, billing, or model access are
207
+ wrong) or request-scoped (this prompt is the problem). One engine-keyed
208
+ signature table classifies by HTTP status first, then provider code, and
209
+ uses text patterns only as the fallback, so `failureReason`, `failureKind`,
210
+ the hint, and the class agree, and an OMP HTTP 400 naming an image count or
211
+ size limit is `request_image_limit`, never an auth failure. The signature
212
+ list is in `docs/troubleshooting.md`. Two new `failureKind` values are
213
+ `lane_known_bad` (a launch refused, below) and `provider_exhausted`
214
+ (workflow only).
215
+ - A persistent, lane-scoped provider failure (rejected credentials, no credit,
216
+ usage limit, no access to the model, missing API key) marks its lane
217
+ known-bad for `providerErrors.knownBadLaneMinutes` (default `15`; `0` turns
218
+ markers off). Until the marker expires, another launch on that lane is
219
+ refused in milliseconds, before any worktree or child exists, with
220
+ `lane_known_bad` and the new exit code `4`; the payload carries `signature`,
221
+ `class`, `hint`, `expiresAt`, `secondsLeft`, and `markedRunId`. The global
222
+ `--force-launch` launches anyway (another persistent failure renews the
223
+ marker), a success clears the marker, and `--dry-run` never refuses.
224
+ `delegate doctor` lists live markers as `knownBadLanes`. Transient,
225
+ request-scoped, and unclassified failures never mark a lane, nor do endings
226
+ Delegate established itself (timeout, stall, output cap, cancel), and a
227
+ Codex usage limit with `codex.fallbackProfile` set stays with the profile
228
+ failover. The `providerErrors` config section is new, and an unknown key in
229
+ it fails as `invalid_provider_errors_config`.
230
+ - A known-bad lane is one engine on one provider, model, and account (the
231
+ Codex failover identity, `CLAUDE_CONFIG_DIR`, or the auth profile), plus the
232
+ credentials the child will see: API keys, tokens, and broker, realm,
233
+ account, and endpoint settings in the environment, and for OpenCode the
234
+ provider keys, base URLs, and `Authorization` headers inside
235
+ `OPENCODE_CONFIG_CONTENT`. They are folded in as a salted hash (the salt is
236
+ `~/.delegate/state/lane-health.salt`, mode 0600), so a bad key on one
237
+ account never refuses a healthy account on the same model, and no key value
238
+ is stored or shown. `doctor` and the refusal print only a short `[credential
239
+ xxxxxxxx]` tag, an account label shaped like an email address is masked, and
240
+ a success clears its marker even when the marker store's lock is stuck.
241
+ - A Codex or Claude work run that fails with a transient provider drop
242
+ (`stream_disconnected` or `provider_unavailable`: Codex's websocket closing
243
+ early, or a 5xx or overloaded Claude reply) and saved its native session is
244
+ continued once, automatically, the way `delegate followup` continues one:
245
+ same session, a new run linked to the first by `followupOf`. The
246
+ continuation's envelope and manifest carry `autoResume` (`automatic`,
247
+ `attempt`, `of`, `trigger`), and the final result is the continuation's.
248
+ There is no second automatic attempt, so a drop in the continuation is
249
+ final. It never applies to safe or call mode, `--pass-through`,
250
+ structured-output runs (the workflow supervisor owns that retry), runs that
251
+ saved no session, or runs in a temporary worktree; a worktree run attaches
252
+ to its own worktree and branch. When the continuation cannot be built the
253
+ first run's result is returned unchanged with `autoResume` set to
254
+ `attempted: false` and a `reason`. It is on by default, and
255
+ `providerErrors.autoResume: false` opts out; workflow children are
256
+ unresumable unless the call passes `resumable=True`, so it reaches them only
257
+ then.
258
+ - Workflow `agent()` returns two new typed failure kinds under
259
+ `on_failure="typed"`: `lane_known_bad` (the child refused to launch because
260
+ its lane is marked known-bad; workflows never pass `--force-launch`) and
261
+ `provider_exhausted` (the call was skipped, with no run and a `run_id` of
262
+ `None`, because its stage already stopped launching on the lane).
263
+ `AgentFailure.provider_error` carries the child's `providerError` so a
264
+ script can branch on the signature instead of parsing prose, and
265
+ `capabilities["providerOutcomes"]` advertises the feature. A stage is one
266
+ `phase()` label and a lane is one engine on one model. When the first
267
+ `providerErrors.stageStopAfter` results (default `3`; `0` turns the stop
268
+ off) of a stage on a lane all fail with the same persistent, lane-scoped
269
+ signature, the rest of that stage's calls on that lane are not launched,
270
+ cells already queued behind the agent cap skip too, and a call with an
271
+ engine fallback chain moves on to its next engine. A success or any
272
+ different result among the first results means the lane is not uniformly
273
+ bad, and transient and request-scoped failures never count. The trip is
274
+ journaled once as `stage_lane_stopped` and each skipped call as
275
+ `agent_lane_skipped`; a skipped call still counts against the workflow's
276
+ agent budget.
277
+ - `delegate capabilities refresh` records per-engine auth health. A successful
278
+ refresh runs the read-only probes named in `providerErrors.authProbes` (by
279
+ default `estate-cursor status` for `cursor` and `estate-omp usage` for
280
+ `omp`) and records what they clearly say as `authHealth`, in its JSON and
281
+ text output and in `~/.delegate/state/auth-health.json`: `ok`, `logged_out`
282
+ (Cursor reports it is not signed in), or `limit_reached` (an OMP quota
283
+ window is at 100%). OMP reports quota per provider and account, so its
284
+ reading also carries `lanes` (`<provider>/account <N>` to `ok`,
285
+ `limit_reached`, or `unknown`); the engine reads `limit_reached` only when
286
+ every lane is exhausted, `partial` when some are, and `unknown` for lanes
287
+ the report does not describe. A probe that is missing, slow, exits non-zero
288
+ without a recognisable message, or prints something unexpected records
289
+ `unknown` with a `reason`; `unknown` is never a failure and never refuses a
290
+ launch, and the probe's raw output is not stored. A subset refresh probes
291
+ only the named engines and keeps the others' last reading, and `delegate
292
+ doctor` shows the last reading as `authHealth`. `authProbes` accepts only
293
+ `cursor` and `omp` (each a command and its arguments, or `null` to turn that
294
+ probe off), and `DELEGATE_AUTH_PROBES=off` (or `0`, `false`, `no`) skips
295
+ every probe.
296
+ - A conformance test requires the parser to accept every documented command
297
+ line. It extracts each `delegate` line from `docs/cli-reference.md` and each
298
+ usage and example line in `COMMAND_SPECS`, expands the usage synopses into
299
+ concrete argv lists (minimal, maximal, each alternative alone, and every
300
+ pair of optional groups), and fails on any the parser refuses, so a synopsis
301
+ that shows mutually exclusive options as independent cannot drift again. A
302
+ separate test runs the real launcher on `mail send --to coordinator -` with
303
+ a piped multi-byte UTF-8 stdin and compares the delivered bytes.
304
+ - The stall watchdog stops runs for named reasons besides idle time. A run
305
+ that streams roughly 100k tokens of model output with no tool activity stops
306
+ as `runaway_output`; only model-output payload characters count, and
307
+ text-stream harnesses such as Devin are exempt. The same tool call failing
308
+ the same way five times in a row, with nothing else completing in between,
309
+ stops as `repeated_tool_failure`; each failure counts in exactly one streak,
310
+ and targets compare whole. A repeated identical call no longer resets the
311
+ idle clock and time spent inside tool calls no longer counts as idle, so a
312
+ fast no-op loop is caught while a slow polling loop is left alone. A changed
313
+ `HEAD` in the execution checkout counts as progress. Every idle stall samples
314
+ the child's process group and records `childActivity` (`cpu_active`,
315
+ `waiting`, or `no_processes`) with a process snapshot, and a pending stall
316
+ kill is re-validated after those blocking diagnostics.
317
+ - A Devin child whose output already ends in a Delegate completion report and
318
+ then goes quiet is stopped as finished (`stoppedAfterCompletion`) instead of
319
+ stalled. Only a `Status: completed` report records exit 0; `failed` or
320
+ `blocked` records a failed run, and a child that echoes the prompt's own
321
+ template line (`Status: completed / blocked / failed`) stalls as before.
322
+ - Tracked launches take `--stall-minutes`, which pins the stall threshold over
323
+ config, the silent-harness default, and `DELEGATE_STALL_MINUTES`. The
324
+ manifest records a pinned value as `stallMinutes`, and `resume` and
325
+ `followup` pass it to the continuation (config and environment defaults are
326
+ still re-resolved). Resume refuses an invalid recorded value and followup
327
+ ignores one, as each already treats `timeoutSeconds`. `describe` lists the
328
+ flag with the other tracked-only launch options.
329
+ - With `DELEGATE_CHILD_NO_PAGE_ASK=1` in the launching environment, each
330
+ tracked child finds a stub `ask` first on its `PATH`. The stub prints a
331
+ refusal and exits 2 (the real `ask`'s relay-down code) instead of paging the
332
+ operator's phone and blocking. It is written into the run's own private temp,
333
+ scratch, or run directory; nothing under `$HOME` is written.
334
+ - An `--isolation none` run whose cwd is strictly inside a git work tree
335
+ records the files it changed elsewhere in that repository as
336
+ `outsideCwdChanges` (repository, count, examples) with a warning. The before
337
+ and after snapshots key each path by status, size, and mtime, skip
338
+ Delegate's own registry, and run git with `--no-optional-locks` so they never
339
+ rewrite the user's index. A repo-root cwd, a non-git cwd, and paths outside
340
+ the repository stay undetected, and the warning says so.
341
+ - Safe and dirty-synced workspace copies name, in a run warning, the paths
342
+ they withhold only because `.git/info/exclude` ignores them. The files are
343
+ still not copied, since `info/exclude` is where private scratch usually
344
+ lives.
345
+ - An omp prompt that names image paths warns that omp will not attach them
346
+ (omp attaches files only from `@path` argv, and Delegate sends the prompt on
347
+ stdin) and points at omp's read tool. The scan runs in linear time.
348
+ - `runs --summary` and `ps --summary` print counts by status, harness, and
349
+ group for every matching run, without rows or manifest loads. Each run is
350
+ counted once across registry roots, the listing's empty-scope and
351
+ status-filter warnings apply, and `--limit` and `--structural` are refused.
352
+ - `wait --json --structural` reduces each run to identity, terminal status,
353
+ result quality, and failure fields, and moves the handle-resolution warnings
354
+ (`bare_handle_stale`, `bare_handle_ambiguous`, `run_target_stale`) to the top
355
+ level. `ok` and the exit code still come from the full view; text output
356
+ ignores the flag.
357
+ - `worktree show` and `worktree remove` accept the worktree path as the
358
+ handle, matched canonically against the records' `executionCwd`; a relative
359
+ path resolves against the `--cwd` workspace. An unknown path fails with
360
+ `unknown_worktree_path` and, like an unknown handle, points at
361
+ `worktree reap --path`. `not_worktree_run` names the run that owns the
362
+ worktree and puts `worktree show` on it first in `nextActions`.
363
+ - A bare harness handle warns `bare_handle_ambiguous`, naming up to five other
364
+ runs, when other runs of that harness share its group or are still running.
365
+ - A cursor model that is a bare family word such as `grok`, and is neither a
366
+ configured `cursor.models` alias nor a catalog selector, resolves to the
367
+ family's newest selector (highest version, non-fast, balanced tier) from the
368
+ discovered catalog, or from the bundled table when discovery has none, with
369
+ a warning. Configured defaults and routes resolve the same way before effort
370
+ routing.
371
+ - Launches warn when the resolved model id is absent from the discovered
372
+ catalog, or from the bundled table when discovery is empty; bundled-table
373
+ warnings fire only for a selector that resembles a listed id. Cursor now
374
+ uses this shared check, so a misspelled id warns without a discovery
375
+ snapshot. A Claude `[1m]` suffix compares by its base, and Claude family
376
+ aliases are exempt. The launch is never refused and the selector never
377
+ rewritten.
378
+ - The `--forbid-commit` implied-worktree note and a missing `codex.profile`
379
+ overlay print to stderr when a run launches, not only in the finalization
380
+ report or `doctor`.
381
+ - A failed safe-mode run whose child reports resolver or reachability errors
382
+ (`getaddrinfo`, `ENOTFOUND`, `EAI_AGAIN`, and similar) warns that safe mode
383
+ may block network access and that research belongs in work mode. Only a
384
+ generic nonzero exit gets the hint; status and failure kind are unchanged.
385
+ - `followup` on a run that a later resume or followup already continued prints
386
+ a note naming that run and the command that continues from it, unless the
387
+ later run shares the source's native session.
388
+ - `describe` and `models --json` report `binary` for every engine, droid
389
+ included. The `models` text view omits a retired droid row that has no
390
+ configured or discovered models and no configured default.
391
+ - Workspace spec for work lanes in a persistent worktree: `--base REF` cuts the
392
+ worktree from REF, `--env NAME=VALUE`/`--env-file PATH` set child variables
393
+ that are recorded privately and re-applied on resume and followup, and
394
+ `--setup CMD` runs in the fresh worktree before the child. A failed setup is
395
+ a typed `workspace_setup_failed` error (`failureKind: workspace_setup`) and no
396
+ child launches. `run --input-json` takes `base`, `env`, and `setup`, and
397
+ workflow `agent()` takes `base=`, `env=`, and `setup=` (capability
398
+ `workspaceSpec`). `workflow run --env/--env-file` records a launch env that
399
+ every resume re-applies.
400
+ - A worktree whose run is not terminal while its launching Delegate process is
401
+ alive is leased: prune, auto-prune, reap, and remove skip it with
402
+ `worktree_leased` even when the child pid is dead.
403
+ - `delegate runs` in a repository's main worktree also lists runs registered in
404
+ its linked worktrees, tagged with `registryWorkspace`.
405
+ - Workflow steps can carry a stable caller-supplied identity. `agent(..., key="...")`
406
+ replays by that key inside the enclosing named scope instead of by position and
407
+ prompt text, so a resumed script that skips settled work or embeds new git heads
408
+ in its prompts still hits its cache. A changed prompt under the same key adopts
409
+ the recorded result and journals `key_prompt_mismatch`. `parallel`, `pipeline`,
410
+ and `workflow` accept `key=` to name their scope. Reusing a key within one run
411
+ raises `WorkflowKeyConflict`. Unkeyed calls keep their positional keys, so
412
+ existing journals replay unchanged.
413
+ - Gate actions: `park_gate(key, result, actions=[...])` pauses for an operator
414
+ decision, and `workflow approve <wfId> --gate KEY --action NAME [--note TEXT]
415
+ [--data JSON]` returns that choice to the script. Undeclared actions are
416
+ refused with the allowed list. A bare `approve` behaves as before. A recorded
417
+ action the resumed script no longer declares is not returned: the gate parks
418
+ again (`gate_action_undeclared`) and asks under the current actions. A second
419
+ live `park_gate()` with the same key in one named scope raises
420
+ `WorkflowKeyConflict`.
421
+ - The workflow `capabilities` global adds `agentKey`, `scopeKey`, and
422
+ `gateActions`.
423
+ - Tracked runs persist the harness-reported token `usage` on the run record, so
424
+ `snapshot`, `run-output`, and `runs --json` expose it without re-reading the
425
+ child's stream (previously it reached only call mode's launch response).
426
+ - A fungible or panel run whose harness reports a served model other than the
427
+ resolved one now carries a `model_substitution` warning naming requested,
428
+ resolved, and served models. Pinned runs already pause on a switch; the
429
+ warning closes the silent case for the default continuity mode.
430
+ - `delegate doctor` warns when a configured `<engine>.defaultModel` (resolved
431
+ through `<engine>.models` aliases first) is absent from the selected profile's
432
+ discovery catalog, naming the nearest discovered selectors. It checks the
433
+ unique targets of configured `<engine>.models` aliases the same way, naming the
434
+ alias key, since a selector an operator wrote down is worth knowing about even
435
+ when no current launch selects it. A cursor launch
436
+ adds the same warning for its selected selector, because cursor-agent resolves
437
+ an unknown selector inside the child. All of them are warnings: an empty or
438
+ missing catalog stays silent, and no configured value is rewritten or refused.
439
+ - `workflow run` warns when it detects it is running inside a systemd unit whose
440
+ `KillMode` will reap the detached supervisor (`control-group`/`mixed`), naming
441
+ the unit and the required `-p KillMode=process`. Previously the unit reported
442
+ success while the workflow was already dead.
443
+
444
+ ### Fixed
445
+ - Missing local profile directories are silently omitted from write-guard
446
+ launcher reopens. Failover state uses `~/.delegate/failover` when the optional
447
+ profile tree is absent, and missing personal profile homes do not read legacy
448
+ cooldown files. Existing profile trees keep their current behavior. The
449
+ development-only `delegate-audit` shim names its optional checkout when absent.
450
+ - `worktree reap` removes linked orphans on Linux again. Its process scan
451
+ refused every removal as `process_scan_unavailable` because Linux hides the
452
+ working directory of the user's own non-dumpable processes (`systemd
453
+ --user`, `ssh-agent`, `sshd-session`), which every login session has. A
454
+ hidden process whose real and effective user and group ids match the
455
+ caller's is now noted in a warning and does not block (the saved ids may
456
+ differ, as for set-group-id `ssh-agent` on Debian); one with other ids (a
457
+ `newgrp` shell) or an unreadable status still refuses. Shells,
458
+ agents, and bubblewrap-sandboxed lanes stay readable and still block
459
+ removal. The test helper that runs fake children also writes the script to
460
+ a file, since Linux caps one argument at 128 KiB.
461
+ - A stale Run's `nextActions` (in `runs` and `snapshot`) now offers `delegate
462
+ resume <handle>` (not for `call` Runs, which cannot resume). The CLI
463
+ reference and troubleshooting guide say what killing a launch command does:
464
+ the launch is the Run's supervisor, the child keeps running on its own until
465
+ it next writes output and then exits, the Run reads `stale`, and `cancel`,
466
+ `resume`, or `followup` recover it. Launch each lane from its own background
467
+ task.
468
+ - The `runs` and `worktree list` text tables head their time-since-activity
469
+ column `idle`; it was labelled `age`, which agents read as run age. JSON is
470
+ unchanged.
471
+ - Parallel Runs launched at the same moment (workflow `parallel()` children,
472
+ a fan-out) no longer fail at random with `unsafe_scratch_directory` ("File
473
+ exists: 'run-scratch'"). Two launches racing to create the shared scratch
474
+ root now both use it; the symlink, non-directory, and foreign-owner checks
475
+ still run on the directory either way.
476
+ - The Claude model-typo refusal is skipped when `CLAUDE_CODE_USE_BEDROCK`,
477
+ `CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`, or `ANTHROPIC_BASE_URL`
478
+ comes from `--env` or `--env-file`, not only from the launching
479
+ environment. The env files are read once per launch, so the check and the
480
+ child see the same values.
481
+ - A Run's `current` no longer keeps showing a provider error after the
482
+ harness has moved on. Pi and OMP show `retrying after provider error
483
+ (429)` while they retry and then the new activity; a Codex `turn.started`
484
+ or OpenCode `step_start` after an error replaces it the same way.
485
+ - `--notify` channel pings work against post 0.9.0, which removed `--anyway`
486
+ from its help. The notifier sends without it and retries once with
487
+ `--anyway` only when post refuses the send as `crossed_send`.
488
+ - The setup-command output tail recorded for a worktree Run can no longer
489
+ reconstruct a secret passed through `--env`/`--env-file`: recorded values
490
+ and their fragments are masked, generic redaction runs between masking
491
+ passes, and the whole pass repeats until nothing changes (a tail that never
492
+ settles is replaced by the mask).
493
+ - The Cursor model-family warning names the selector the operator typed and
494
+ the routed selector actually passed to Cursor.
495
+ - Every harness caps its map of tool calls still waiting on a result at 256
496
+ entries (Claude, Kimi, Grok, and Cursor kept them all), and a completion
497
+ report status line may end in a semicolon (`Status: completed;`).
498
+ - `worktree remove` of a parent no longer orphans worktrees of Runs launched
499
+ inside it: clean, merged nested worktrees are removed first, and dirty or
500
+ unmerged ones refuse with `nested_worktrees_block_remove` and a per-nested
501
+ command (`--force` does not reach them). Reap refuses a pool path holding
502
+ nested worktrees (`nested_worktrees_present`), a nested Registry that
503
+ appears mid-removal refuses with `nested_registry_appeared`, and an
504
+ unreadable nested Registry refuses even with `--kill-live`.
505
+ - A Codex workflow stage whose schema has an object with no declared
506
+ properties (including through a local `$ref`) no longer fails every
507
+ attempt: it uses the prompt path instead of strict mode, which closed the
508
+ object to zero keys. A direct `codex --output-schema` with such a schema is
509
+ refused at preflight with `invalid_output_schema` naming the path.
510
+ - A dry-run `workflow resume` no longer cancels or reaps a stale scope child,
511
+ changes status, persists a budget, or records an approval; it only
512
+ journals. `workflow approve` on a failed workflow points at `workflow
513
+ resume`, and on a running one says the gate is not open yet.
514
+ - A relative `--prompt-file` falls back to `--cwd`, and the not-found error
515
+ names both paths tried.
516
+ - The unread-mail scan cannot block on a FIFO named like mail; the shared
517
+ bounded record reader opens non-blocking.
518
+ - Small surface fixes: the Registry writes `.delegate/.gitignore`;
519
+ `--include-dirty` is a warned no-op in safe mode and reports `includeDirty:
520
+ false`; `runs show` names the real readers; a timed-out `wait --json` warns
521
+ and names `--structural`; `no_matching_worktrees` names the Registry it
522
+ searched.
523
+ - A `--mail-push` Codex run no longer dies before the model starts. Delegate
524
+ injected `-c hooks=true`, which Codex 0.157 rejects at config load because
525
+ `hooks` is a table; it now passes `--enable hooks` (the same as `-c
526
+ features.hooks=true`) and does not add it twice when it is already present.
527
+ - `--notify` room pings work against post 0.9.0. The notifier no longer passes
528
+ `--allow-self`, which post now rejects as an unexpected argument, and the
529
+ message-id match accepts post's room ids (which have no microsecond field)
530
+ as well as channel ids, so `messageId` is no longer empty for a room ping. A
531
+ room ping is a workspace fan-out that never reaches its own sender, so
532
+ `--notify` naming your own room reaches the room's other participants and
533
+ not the launching session; the troubleshooting guide now says so.
534
+ - On a Python older than 3.11 (Xcode's 3.9, for example), `bin/delegate.py`
535
+ and the `delegate_agent` package now exit `2` with a message naming the
536
+ interpreter version and executable they found, instead of a raw
537
+ `ImportError` from inside the package. The tracked
538
+ `bin/delegate-profile-shim` picks a 3.11+ interpreter itself (`python3`,
539
+ then `python3.14` down to `python3.11`, then Homebrew's, then
540
+ `/usr/local/bin/python3`), honors a validated `DELEGATE_PYTHON` (an override
541
+ older than 3.11 is refused, not bypassed), and exits `2` with a plain
542
+ message when none is found.
543
+ - The Codex and `post` argv Delegate builds is now tested against the
544
+ installed binaries (`tests/test_real_binary_contracts.py`), which is how the
545
+ two dead flags above shipped. A `codex` or `post` on `PATH` that fails
546
+ `--version`, times out, or cannot register the throwaway room now fails the
547
+ suite with its command, exit code, and output instead of skipping it; a
548
+ class skips only when its binary is absent or
549
+ `DELEGATE_SKIP_REAL_BINARY_CONTRACTS` is truthy, and says why.
550
+ `pytest-xdist` workers now see the real HOME through
551
+ `DELEGATE_TESTS_ORIGINAL_HOME`.
552
+ - A run that fails keeps what the child had produced. A failed, timed-out, or
553
+ (opt-in) capped run's completion report now quotes the child's last
554
+ substantive assistant text, bounded and redacted, under "Partial output
555
+ recovered before the run stopped. This is not a completion report", as a
556
+ cancelled run's already did. Pi, OMP, and OpenCode keep that text across
557
+ turns and steps, so a long review followed by a tool turn and a provider
558
+ error is still recoverable (OpenCode used to clear it at every step). A
559
+ call-mode timeout or output overflow, including a child that had already
560
+ exited before Delegate noticed the overflow, returns the buffered draft as
561
+ `diagnostics` on the error (also flattened into the JSON error):
562
+ `partialText` (redacted and bounded), `stdoutTail`, and `stderrTail`; text
563
+ mode prints the partial text to stderr.
564
+ - `followup` errors now say what is true. A source with no recorded native
565
+ session (launched with `--no-resumable`, before Codex and Claude work runs
566
+ saved their session by default, or as a workflow `agent()` call without
567
+ `resumable=True`) is refused with `session-missing` and pointed at `delegate
568
+ resume <handle>`, no longer told to relaunch with `--resumable`. A session
569
+ that was saved but cannot be found when resumed fails as `session_expired`
570
+ (`failureKind: session_lost`); the message names the usual cause, a launch
571
+ under a different account than the one holding the session, and offers
572
+ `delegate resume`.
573
+ - `resume` and `followup` refuse a known option of that command that appears
574
+ in the prompt tail (`delegate followup x fix it --dry-run`) with
575
+ `option_after_handle` before anything launches, instead of sending it to the
576
+ child as prompt text while the run launches with the defaults. Put `--`
577
+ before the prompt to send such a token as literal text. `followup` accepts
578
+ options on either side of the handle but only before the prompt text, and
579
+ `resume` only before the handle; an option-shaped token that belongs to
580
+ another command keeps the existing warning.
581
+ - A workflow structured retry on Codex or Claude whose resumed session cannot
582
+ be found (`session_lost`, reason `session_expired`) now falls back to a
583
+ fresh relaunch in the same worktree instead of ending, without spending a
584
+ retry; the journal records `agent_structured_retry` with `strategy:
585
+ "relaunch"` and `fellBackFrom: "resume"`. A fresh child redoes the task, so
586
+ the fallback relaunches only over a worktree known to be untouched, and
587
+ otherwise the call is refused (`agent_structured_retry_refused`) as
588
+ `work_changed_session_missing` when an earlier attempt already changed the
589
+ worktree, or `work_state_unverified` when that could not be checked. The
590
+ work summary now records `fileInspectionStatus` next to
591
+ `commitInspectionStatus` (each `verified` or `unverified`), because a failed
592
+ `git status` used to read as zero changed files; `noChanges` is true only
593
+ when both are `verified` and show nothing. In work mode both the prior
594
+ attempt and the resumed attempt must carry a verified summary that reports
595
+ no changes, and a missing summary, or one from a Delegate that predates
596
+ `fileInspectionStatus`, counts as unverified. Safe and call children have no
597
+ summary and keep the fallback.
598
+ - `worktree reap --path P --older-than N --yes --force` now removes a pool
599
+ entry that has no run record but that Git still links; `reap` used to skip
600
+ it as `live_backlink` even with `--force`, and the only way out was `git
601
+ worktree remove --force` by hand. Every check is re-run under the locks
602
+ immediately before removal. The pool scan must have no warning for the entry
603
+ (an entry with broken-looking metadata changed within the last 15 minutes,
604
+ or whose metadata cannot be read, is refused); Git must still list the path
605
+ as a worktree of its source repository; no Registry may hold a record for it
606
+ (`record_owns_path`, including a run that claimed it after planning, or
607
+ `record_in_other_registry` with the `--cwd` to run from); there must be no
608
+ uncommitted work by the definition above (`dirty`, `dirty_unknown`) unless
609
+ `--discard-uncommitted` is passed; no process may have its current directory
610
+ inside the path (`process_cwd_inside`, from one pass over `/proc` or `lsof
611
+ -d cwd`, never `lsof +D`) unless `--kill-live` is passed; and changed ledger
612
+ files are saved first. A process scan that cannot run, or cannot read one of
613
+ your own processes, refuses as `process_scan_unavailable`; other users'
614
+ processes are noted in a warning and do not block. The branch is kept, and
615
+ without `--force` the entry is still skipped as `live_backlink`, now with a
616
+ hint naming `--force`.
617
+ - Persistent worktrees no longer replace committed symlinks with placeholders.
618
+ The replacement showed up as typechanges that the next `git add -A` would
619
+ commit. A symlink committed to the repository is left as Git checked it out,
620
+ absolute or not; only an untracked symlink that points outside the source,
621
+ or at gitignored content, is replaced by a placeholder as it is mirrored in,
622
+ and the launch warning names it. Safe mode is unchanged and still blocks
623
+ every external symlink in its throwaway copy.
624
+ - `worktree show` on a followup, resume, or attached run names the run that
625
+ owns the worktree, following the lineage up through followups and resumes
626
+ (up to 16 hops, with a cycle guard) and putting `delegate worktree show
627
+ <owner>` first in `nextActions`.
628
+ - Two false or misleading model warnings are gone. Cursor labels are compared
629
+ after the context-window token (`256K`, `1M`) is dropped, so
630
+ `grok-4.7-xhigh` served as `Grok 4.7 256K Extra High` is the requested model
631
+ and not a `model_substitution`. A pinned call whose stream named a served
632
+ model is no longer told it "could not be verified"; that warning now fires
633
+ only when the stream was silent.
634
+ - Usage synopses in `--help`, `describe`, and `docs/cli-reference.md` now
635
+ match what the parser accepts; the parser was right in each case. `claude
636
+ call` takes `[--read-only|--pure]`, not both. `runs` and `ps` take
637
+ `[--summary | [--limit N] [--structural]]`, because `--summary` prints
638
+ counts and no rows and is refused with either. `snapshot` and `run-output`
639
+ take `(<handle>|--latest HARNESS)`. `run-output` takes `[(--stdout|--stderr)
640
+ [--raw | [--tail N] [--max-chars N]]]`, with a second usage line,
641
+ `[--no-redact] (--raw | --tail N)`, for the standalone `--raw` and `--tail
642
+ N` forms. `worktree remove` takes `[--keep-branch | [--force-branch]
643
+ [--force]]`.
644
+ - `describe --summary` is now described as the command index plus config
645
+ resolution, launch options, profiles, and workflows, larger than the default
646
+ and `--overview` views and smaller than `--full`, not as compact.
647
+ - The workflow supervisor relays `SIGINT` to its children the way it does
648
+ `SIGTERM` and `SIGHUP`, and journals `supervisor_signalled`. `SIGINT` used
649
+ to reach the interpreter's default handler, which raised `KeyboardInterrupt`
650
+ and marked the workflow failed without cancelling its children; they ran on
651
+ unowned until a later resume sealed them.
652
+ - The `agent_timeout` journal row written when a `followup()` call times out
653
+ now carries `key`, `label`, and `scope`, as the `agent()` timeout row does,
654
+ so a journal with several follow-ups says which step died.
655
+ - The workflow docs no longer say a resume adopts running children. A resume
656
+ cancels every child the previous attempt still had running and relaunches
657
+ that work; adoption covers only children that already finished (or that this
658
+ supervisor lifetime is itself still launching). Waiting on a live orphan and
659
+ taking its result is deferred work, not current behavior.
660
+ - `describe --full` documents the workflow DSL the runtime actually injects.
661
+ Its `globals` list is read from the runtime's new `WORKFLOW_DSL_GLOBALS`,
662
+ which the injector also checks, so it now names `park_gate`, `reject`,
663
+ `soft_park` and its helpers, `structured_attempt`, and `dry_run` (aliases in
664
+ `globalAliases`). `capabilities` is derived from the runtime's capability
665
+ table, adding the `agentKey`, `scopeKey`, `gateActions`, and `workspaceSpec`
666
+ entries the old literal lacked, and is also given structured as
667
+ `capabilityVersions`. The `agent()` signature is rendered from the runtime
668
+ method, so it gains `resumable`, `on_failure`, `key`, `base`, `env`, and
669
+ `setup`; the `pipeline`, `parallel`, and `workflow` entries document `key=`,
670
+ and `judges` documents per-entry dicts.
671
+ - A stdout or stderr line handler that raises (a registry lock timeout while
672
+ persisting progress, a parser bug) no longer kills the drain thread, which
673
+ stopped consuming the child's pipe and could strand the child. The drain
674
+ keeps reading to EOF, logs a `stream.handler_failed` event, and surfaces a
675
+ run warning that retried attempts carry forward.
676
+ - `cancel` no longer fails with a raw `TimeoutError` while another process
677
+ holds the registry lock. When the lock wait runs out it reads the record
678
+ unlocked, re-reads it with any pending finalize WAL overlaid just before
679
+ signalling, and signals only a launched child generation whose start identity
680
+ matches the run; a terminal result refuses as `run_already_terminal` and a
681
+ changed generation as `cancel_target_changed`. It then makes one bounded
682
+ attempt to record the cancelled outcome, reporting `registryLockBypassed`
683
+ with an unrecorded warning if the lock is still held, and refuses with
684
+ `registry_lock_busy` for an unlaunched record, a setup group, or a stale
685
+ seal.
686
+ - A bwrap safe child in a uv project without a `.venv` gets
687
+ `UV_PROJECT_ENVIRONMENT` pointed inside the run's own writable temp
688
+ directory, so `uv run` no longer fails creating `.venv` under the read-only
689
+ workspace bind. An existing `.venv` or an explicit `UV_PROJECT_ENVIRONMENT`
690
+ is left alone.
691
+ - Resume onto codex or claude keeps a source run's explicit `resumable`
692
+ opt-in, so `followup` on the resumed alias works (any other source now takes
693
+ the resumable-by-default launch setting); a cross-engine resume onto an
694
+ engine that cannot keep it drops the opt-in and says so.
695
+ - Resume and followup that reuse the source run's auth profile now say the
696
+ profile was inherited from the source run instead of warning about an
697
+ `--auth-profile` flag the user never typed.
698
+ - The suite resolves the production compact-temp root the way runs do. On
699
+ macOS it named `/var/tmp` where runs record `/private/var/tmp`, so six tests
700
+ failed and every subprocess run's temp directory leaked.
701
+ - A workflow no longer cancels its own live children as stale. Two `agent()`
702
+ calls from plain script threads could share one positional scope, and the
703
+ second call cancelled the first's running child. Stale-scope cancellation now
704
+ targets only children an earlier supervisor lifetime started (`agent_started`
705
+ records the lifetime's `incarnation`).
706
+ - Workflow adoption relaunches instead of waiting on or failing over a key's
707
+ latest child run when that run is cancelled, stale, or never launched
708
+ (`agent_adopt_skipped`).
709
+ - Resume seals a workflow child that never published a pid (stuck at `running`
710
+ or `creating_isolation`) once it has been idle for 300 seconds and its
711
+ recorded launcher (`launcherPid`) is dead or its pid was reused, instead of
712
+ refusing with `workflow_children_unsealed` forever. The runner re-reads the
713
+ record under the registry lock before every process start, the first included,
714
+ and publishes the child's pid only over a record that is neither terminal nor
715
+ cancel-requested, so a sealed launch never gains a child.
716
+ - `workflow run --dry-run` executes the script with its working directory set to
717
+ the `--cwd` workspace, as a real run's supervisor does. A dry run that times
718
+ out leaves the workspace as the working directory, because its abandoned
719
+ script thread may still be resolving relative paths.
720
+ - Persistent-worktree change accounting lists untracked files individually
721
+ (`git status --untracked-files=all`), matching the per-file launch-seeded
722
+ digest map. A seeded untracked directory used to be reported as one collapsed
723
+ entry the seeded filter could never match, so a quiet `work` run claimed a
724
+ change the child never made and reported success, a structured child with
725
+ unparseable output was refused as changed instead of relaunched, and the
726
+ worktree was retained as dirty. `workSummary.changedFiles` and
727
+ `rawChangedFilesCount` count files rather than directories for untracked
728
+ trees.
729
+ - A bare `429`/`rate limit` diagnostic classifies as `usage_limit` only when
730
+ account-context wording (`quota`, `usage`, `billing`, `subscription`,
731
+ `account`, `credit`) appears on the same line within 80 characters of it, in
732
+ either order. An unrelated `memory usage: 82%` line or a wrapped command's
733
+ `usage:` help text no longer pairs with a transient 429, which had marked the
734
+ run a quota failure, suppressed the structured correction retry, and pointed
735
+ operators at credential rotation.
736
+ - Codex work runs recover the last substantive agent message as the completion
737
+ report when the stream never sealed `turn.completed`, instead of classifying a
738
+ complete report as `resultQuality=empty`. A progress preamble is still not
739
+ adopted.
740
+ - `run-output` no longer claims no recoverable final message exists while the
741
+ child wrote stdout; the diagnostic states the byte count and names the command
742
+ that reads it.
743
+ - Workflow child launches that fail before publishing a run record record the
744
+ exit code and a bounded, redacted stderr tail on the attempt outcome and on a
745
+ new `child_stdout_unparsed` event (previously only the JSON decode error of
746
+ empty stdout was kept). `agent_structured_retry` carries the tail too. This
747
+ covers a child that exits `0` after writing empty or non-JSON stdout, on both
748
+ the initial `agent()` launch and `followup`.
749
+ - An empty `runs`/`ps` listing now adds the workspace-scope warning without
750
+ requiring `--group`/`--harness`, so a bare `runs --running` in a directory
751
+ with no Registry no longer prints an empty table that reads as dead lanes.
752
+ `wait --group` on a group stored in another workspace reports the same hint
753
+ and names the searched Registry.
754
+ - `workflow_not_found` errors for `status`, `run --resume`, and id-based verbs
755
+ name the searched workflow root and the `--cwd` form, so "stored elsewhere"
756
+ is distinguishable from "gone". The workflow profile-drift refusal names the
757
+ differing identity keys with their pinned and current values.
758
+ - `models <engine> --summary` points at `delegate help models`, and the focused
759
+ help states that `--summary` takes no engine argument.
760
+ - The linked-worktree registry-lock guard verifies suite ancestry before
761
+ reporting a violation, so a concurrent unrelated Delegate launcher holding the
762
+ same file is no longer reported as a suite escape.
763
+ - Test bootstrap pins `MISE_DATA_DIR`/`MISE_CACHE_DIR` suite-wide, so a
764
+ subprocess-bearing test no longer makes the mise python shim download an
765
+ interpreter into a temporary HOME and pollute stderr.
766
+ - The suite removes the compact child temp directory each tracked run manifest
767
+ records before it tears that temporary registry down, and asserts no test
768
+ leaves a net directory under the production owner root. Previously any test
769
+ that launched a real Delegate subprocess created `/var/tmp/dlg-<uid>/<token>`
770
+ that only `runs prune` could ever remove, and the temporary registry those
771
+ paths were derived from was deleted first. The run's own recorded child is
772
+ settled first: a live child whose command line carries one of the paths its
773
+ manifest names gets a bounded grace to reach its own exit (so a fixture child
774
+ writing through `$TMPDIR` finishes), and only then, or after being reaped, is
775
+ the exact recorded directory removed.
776
+ - `run-output`, `runs`/`ps`, and `snapshot` reconstruct a persisted run's
777
+ `usage` from one allowlisted normalized shape instead of copying the stored
778
+ object, so a record rewritten after the run (a non-isolated child shares the
779
+ workspace) cannot inject unknown keys, an unknown basis, or credential-shaped
780
+ text into a payload or the `usage:` line. A record whose basis is not one a
781
+ producer emits contributes no usage at all.
782
+ - The served-model comparison behind the `model_substitution` warning uses the
783
+ same harness-aware identity rules as the pinned check: OMP's provider-qualified
784
+ `provider/model` observation, a Cursor display name, and a Claude family alias
785
+ are the requested model, not substitutions. A clean OMP lane is no longer
786
+ labelled as substituted.
787
+ - Model warnings redact their model values: `model_substitution`,
788
+ `pinned_continuity_unverified`, and the configured-model/alias catalog warnings
789
+ can carry an operator- or provider-supplied selector, and all of them reach
790
+ `state.json`, `delegate.doctor.v1`, or the completion report. A
791
+ credential-shaped selector is masked instead of persisted.
792
+ - Workflow child launches that fail before publishing JSON carry the same
793
+ evidence on the *initial* `agent()` attempt as on followups: the outcome
794
+ records the exit code and a bounded, redacted stderr tail, and stdout that
795
+ never parsed emits `child_stdout_unparsed`. An exit-zero child whose envelope
796
+ is unusable is now reported as `invalid_envelope` rather than `nonzero_exit`.
797
+ - `workflow run` detects the systemd unit from the cgroup component that owns
798
+ the current process instead of any ancestor `.service` in the path, so an
799
+ ordinary interactive session under `user@<uid>.service` no longer receives the
800
+ severe `KillMode` warning. A unit under the per-user manager is queried through
801
+ `systemctl --user` rather than read as a system-manager lookup failure.
802
+ - The track's child `TMPDIR`/`TMP`/`TEMP` now points at a separate per-run
803
+ short-path directory (`/var/tmp/dlg-<uid>/<token>`, recorded as
804
+ `manifest.tempPath`) instead of the run scratch. The scratch path carries the
805
+ registry hash and run id, so a child binding a Unix socket under it overran
806
+ `sun_path`; the new root is short by construction, stays owner-only, is granted
807
+ as a second writable root in the Codex read-only permissions profile, is bound
808
+ read-write inside the optional bwrap boundary, survives failure and
809
+ cancellation like the scratch, and is removed only by the same run-owned prune
810
+ path. Runs recorded without a `tempPath` prune as before.
811
+ - The completion report carries a `model_substitution` warning and the run's
812
+ normalized token usage in a labelled delegate-authored block above the child's
813
+ text, and `run-output` (JSON and text) projects the persisted usage. A
814
+ substituted lane no longer reads as the requested model's clean work in the
815
+ report artifact, while the child's report text and its
816
+ `completionReportSource` attribution are unchanged.
817
+ - The `dev` extra ships `setuptools` and `wheel`, making the documented
818
+ `python -m build --no-isolation` form work instead of failing with
819
+ `BackendUnavailable`. The isolated build path is unchanged.
820
+
821
+ ### Changed
822
+
823
+ - The work write guard no longer protects `~/Code` or sibling checkouts by
824
+ default (`isolation.writeGuard.codeRoot` now defaults to `null`). Named
825
+ credential stores, profile homes and the installed Delegate runtime retain
826
+ their existing protection and re-open rules. Set `codeRoot` or `add` to opt
827
+ in to checkout protection. Codex's native sandbox retains its own write limits.
828
+ - Work-mode prompts gain two sentences saying nobody can approve during a Run
829
+ and the task is the approval unless the prompt asks only for a plan,
830
+ review, or read-only answer. Safe and call prompts are unchanged.
831
+ - Runs at `xhigh`, `max`, or `ultra` effort default to a 20-minute stall
832
+ window instead of 8, unless `--stall-minutes`, config, or
833
+ `DELEGATE_STALL_MINUTES` sets one; dry run, manifest, and snapshot report
834
+ `stallWindow` with its source.
835
+ - The `broker_binding_inactive` hint says one refusal while siblings launch is
836
+ usually launch-slot contention (relaunch once, with `--force-launch` if the
837
+ lane is marked) and a repeat means the binding is inactive.
838
+ - OMP alias objects with unknown keys name the allowed keys and point effort
839
+ synonyms at `thinking`; `help omp` carries a valid example. The
840
+ `cursor_auth_required` hint names the auth realm the Run used and the
841
+ `--auth-profile` alternative.
842
+ - **The tracked output cap is now off by default.**
843
+ `<engine>.trackedStreamMaxBytes` used to default to 16 MiB (64 MiB for Pi
844
+ and OMP) and stopped a run as `output_limit_exceeded` when a stream passed
845
+ it. It now defaults to `null`: a verbose run is never killed, truncated, or
846
+ hidden from the parser for its size, and `stdout.log` keeps the whole stream
847
+ and is flushed on every write, so its size and modification time follow the
848
+ child. Set a positive integer to opt an engine back into a cap; the
849
+ `output_limit_exceeded` message then names the key to raise or clear. OMP's
850
+ 256 MiB transport and 16 MiB per-record ceilings apply only with an opted-in
851
+ cap, and call mode keeps its own 16 MiB caps. Runaway streams remain the
852
+ stall watchdog's job (`runaway_output`). The 500-line limit in the
853
+ `events.jsonl` mirror was never a limit on `stdout.log`; its
854
+ `stream.lines_truncated` marker now says the child keeps running and
855
+ `stdout.log` keeps the full stream.
856
+ - A Devin answer is held in memory as a bounded excerpt. Devin prints plain
857
+ lines with no message boundaries, so Delegate treats everything it prints as
858
+ one running answer; past 30,000 characters that answer is the first 20,000
859
+ and the last 10,000 with an `[N chars omitted]` marker between them.
860
+ `assistantText`, the completion report, and a failed run's recovered partial
861
+ output read the excerpt, `assistantTextChars` keeps the full length, and
862
+ `stdout.log` keeps the full stream. The answer used to be rebuilt as one
863
+ string on every line, which was quadratic and, with the cap off, held the
864
+ whole run in memory.
865
+ - **Codex and Claude `work` runs are now resumable by default.** They save
866
+ their native session unless the launch says `--no-resumable` (JSON
867
+ `resumable: false`) or the engine config sets `codex.resumable` or
868
+ `claude.resumable` to `false` (both default `true`), so `delegate followup`
869
+ works without remembering `--resumable`. `--resumable` on a launch still
870
+ beats the config key, and `resume` takes the same default and
871
+ `--no-resumable`; a resume source recorded as exactly `true` keeps it, and
872
+ any other source takes the launch default. The default outranks
873
+ `codex.ephemeral` and `claude.noSessionPersistence`, which now apply only to
874
+ runs that are not resumable. Safe runs, call runs, `--pass-through` runs,
875
+ other engines, and workflow `agent()` children (unless the call passes
876
+ `resumable=True`) are unchanged, and `--no-resumable` is a quiet no-op
877
+ there. A succeeded persistent-worktree run that is resumable keeps its
878
+ worktree (`worktreeRetained: "resumable_session"`) instead of retiring it
879
+ when clean, so `followup` and `resume` can re-enter it; a fan-out of these
880
+ runs leaves one retained worktree per succeeded run until `worktree prune
881
+ --merged`, `worktree gc`, or `worktree remove` cleans up. Launch with
882
+ `--no-resumable` when a run's worktree should retire itself.
883
+ - **`--force` no longer overrides a live run on the worktree commands.**
884
+ `worktree remove`, `worktree prune`, and `worktree reap` refuse a worktree
885
+ that a live owner still holds (`run_active`, `run_not_terminal`,
886
+ `process_group_alive`, `worktree_leased`) even with `--force`, and the
887
+ refusal names the new `--kill-live`, the only override, which removes the
888
+ worktree out from under the run. `--kill-live` does not discard uncommitted
889
+ work or force the branch by itself. Removal is also refused for a running
890
+ run recorded in the worktree's own `.delegate/` registry
891
+ (`nested_run_active`) and for a nested registry, or a run in it, that cannot
892
+ be read (`nested_registry_unreadable`), each overridable only by
893
+ `--kill-live`. A skipped entry in `prune` and `reap` JSON carries the same
894
+ hint.
895
+ - Uncommitted work has one definition across `worktree remove`, `prune`,
896
+ `reap`, and completion retirement: what the lane actually changed. Files the
897
+ launch seeded from a dirty source that still match their digest, and the
898
+ ledger paths in `worktrees.retirementIgnoreGlobs` (default `.beads/**` and
899
+ `.papercuts.jsonl`), do not count, so a worktree that `prune` plans is one
900
+ `remove` accepts and a refusal names only the lane's own paths. Ledger edits
901
+ are saved before any removal: changed ledger files are copied and
902
+ byte-compared into `<Registry>/salvage/<worktree>-<UTC timestamp>/`. The
903
+ result reports `salvagePath` and `salvagedPaths`, the text output prints a
904
+ `saved N changed ledger file(s)` line, and completion retirement records
905
+ `worktreeSalvagePath` on the run. Deleted ledger files and the old name of a
906
+ renamed one are recorded in a `MANIFEST.tsv` (status, path, old path) in the
907
+ same directory and reported as `salvageRemovedPaths`. A copy that fails, or
908
+ a `git status` that cannot run, refuses the removal as
909
+ `ledger_salvage_failed`. Delegate never deletes a salvage directory, and a
910
+ source-gone `reap` deletes the path without a copy.
911
+ - **An OMP `provider/model` id you type is now pinned by default.** A
912
+ `provider/model` given as `--model`, input-JSON `model`, a positional raw
913
+ id, or a literal workflow `agent(model=...)` behaves as `--continuity-mode
914
+ pinned` unless you name a mode. Delegate launches it with a private
915
+ `--config` overlay that sets `retry.modelFallback` and
916
+ `retry.usageAwareFallback` to `false`, so OMP's own retry chains cannot move
917
+ the run to another provider (retrying the same model is unaffected, the
918
+ overlay beats your `config.yml`, and a dry run shows `--config <omp config
919
+ overlay>`). If another provider or model is served anyway, the run fails as
920
+ `model_continuity_paused` with a message naming what OMP tried to serve, for
921
+ example `requested opencode-go/glm-5.3, but omp tried to serve
922
+ fireworks/glm-5p3`. A delegate alias (a key of `omp.models`) and
923
+ `omp.defaultModel` stay `fungible`, so multi-subscription failover keeps
924
+ working; the run records and warns (`model_substitution`) which provider
925
+ answered, and `--continuity-mode pinned` on an alias pins it.
926
+ `--continuity-mode fungible` or `panel` on a typed id opts out of the
927
+ overlay. Workflows have no continuity passthrough yet, so there a literal id
928
+ is pinned and an alias is fungible.
929
+ - An `omp` model name that is not an `omp.models` alias and not a
930
+ `provider/model` selector fails with `invalid_alias` once a discovered omp
931
+ catalog exists, unless it is exactly a catalog model id; the error lists the
932
+ configured aliases and the nearest catalog selectors. OMP resolves a bare
933
+ name by fuzzy match against its own catalog and can serve a different
934
+ provider (a retired alias, `kimi`, resolved to `fireworks/kimi-k3`), so
935
+ Delegate refuses instead of passing it through. With no catalog the run
936
+ proceeds with a warning; `delegate capabilities refresh` lets bare names be
937
+ checked.
938
+ - **Claude `work` runs and their followups now run with background tasks
939
+ disabled.** Delegate sets `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` and adds
940
+ `--disallowedTools Monitor`, because a headless child ends when the model
941
+ stops and a background task or Monitor dies with the session. As a long gate
942
+ must then run in the foreground, Delegate also sets
943
+ `BASH_DEFAULT_TIMEOUT_MS` and `BASH_MAX_TIMEOUT_MS` to the run's own
944
+ `--timeout`, never below two hours (a run with no `--timeout` gets two
945
+ hours), so Claude Code's 2-minute default and 10-minute maximum do not cut a
946
+ foreground command off. `claude.disableBackgroundTasks: false` turns all of
947
+ it off. An auth profile's `env` or a workspace `--env` cannot undo these
948
+ variables: Delegate keeps its own values and warns `profile env NAME
949
+ ignored` or `workspace env NAME ignored` when the value differed. Every
950
+ framed `work` and `safe` prompt also carries a two-sentence rule that ending
951
+ the turn ends the run and that long jobs must run in the foreground and
952
+ finish before the final message; verbatim slash pass-through prompts are not
953
+ rewritten.
954
+ - **A finished run's scratch is now reclaimed on its own age.** Scratch, its
955
+ sidecars (for example mail-push engine homes), and the compact child temp
956
+ used to be removed only when the run record was pruned, so finished runs
957
+ kept their disk until then. The ambient retention pass now removes them for
958
+ terminal runs older than `tracking.retention.scratchDays` (default `3`; `0`
959
+ reclaims as soon as a run is terminal), keeps the run record, and stamps
960
+ `scratchReclaimedAt` and `scratchReclaimedBytes` into the run state;
961
+ `snapshot` shows both, and prints a `scratch reclaimed:` line in text.
962
+ `delegate runs reclaim [--older-than DAYS] [--dry-run]` does the same on
963
+ demand and lists sizes. Running, stale, and already-reclaimed runs are
964
+ skipped, and a run that fails is reported without stopping the others.
965
+ Reclaiming applies the recorded-path and ownership checks that pruning uses,
966
+ removes entries one at a time without following symlinks, and refuses a tree
967
+ that holds another owner's entry or crosses onto another filesystem. The
968
+ ambient pass spends at most 20 seconds, counted inside the walk and the
969
+ removal; a run it could not finish gets no marker, is reported as
970
+ `budget_exhausted`, and implicit reclaim then waits ten minutes before
971
+ continuing. `runs reclaim` has no budget or cooldown, and still works when
972
+ `tracking.retention.enabled` is `false`, which stops the ambient pass.
973
+ - `--forbid-commit` now works in every isolation mode, including `--isolation
974
+ none`, which used to be refused; with isolation omitted it still implies
975
+ `--isolation worktree`. Delegate creates run-owned git hooks (`pre-commit`,
976
+ `prepare-commit-msg`, `commit-msg`, and `pre-merge-commit`) that refuse, and
977
+ points `core.hooksPath` at them through one variable,
978
+ `GIT_CONFIG_PARAMETERS`. Codex's default environment filter drops names
979
+ containing `KEY`, `SECRET`, or `TOKEN`, which left the indexed
980
+ `GIT_CONFIG_COUNT` form without its keys and broke every git command;
981
+ `GIT_CONFIG_PARAMETERS` survives it. The post-exit check stays as the
982
+ backstop: it fails the run if commits remain ahead of the creation base (the
983
+ HEAD recorded at launch for `--isolation none`) or if the checkout's own
984
+ HEAD reflog shows a commit made since launch and left behind, on a side
985
+ branch or reset away, for in-place and worktree runs. An unreadable reflog
986
+ with nothing found otherwise leaves the policy unverified, which also fails
987
+ the run. The hooks are a tripwire, not a wall: `git -c
988
+ core.hooksPath=/dev/null commit` or `git commit-tree` bypasses them, and the
989
+ post-exit check can be defeated on purpose too.
990
+ - A mail send that reaches no inbox fails with `mail_not_delivered` (exit 1)
991
+ instead of returning `ok` with exit 0. The message names each recipient's
992
+ outcome and reason and states the eligibility rule, the diagnostics carry the
993
+ `msgId` and ledger rows, and the sent ledger is still written.
994
+ - `--auth-profile` precedence (flag, then environment, then config default)
995
+ now selects the config file as well as the profile: `--auth-profile
996
+ work|personal` with an existing overlay selects `config.NAME.json`, its keys,
997
+ and the child's `AI_PROFILE`, and warns when that replaces an ambient
998
+ `DELEGATE_CONFIG` or a different `AI_PROFILE`. A flag whose overlay is
999
+ missing warns that the environment's config and keys stay in effect, and the
1000
+ resolver warns when an inherited detection variable overrides the config's
1001
+ `profiles.default`. The profile shim now finds the flag anywhere before `--`,
1002
+ keeps the last occurrence, and strips whitespace, as the Python parser does.
1003
+ - Errors point at the command that does what was asked. `runs`/`ps` given a
1004
+ run id, harness alias, `--id`, or `--alias` point at `snapshot` and
1005
+ `run-output`; unknown `show`, `status`, `info`, `inspect`, `logs`, and
1006
+ `output` subcommands suggest the per-run commands; `mail send --body`,
1007
+ `--message`, and `-m` explain that the body is positional, `--file`, or `-`
1008
+ (a bare `-` now reads stdin, as help already promised); and `followup` given
1009
+ a resume-only option says the option belongs to `resume` and shows the
1010
+ resume form.
1011
+ - **Exit-code semantics: a child's exit code 0 no longer means success on its
1012
+ own.** One outcome function now decides every run's `ok`, `status`, exit code,
1013
+ persisted record, `wait` result, and workflow `agent()` result, so these can
1014
+ no longer disagree. A run now fails with a nonzero exit, and `ok: false`, in
1015
+ three cases where it used to report success. The first is a tracked child
1016
+ that exits 0 without assistant text (`failureKind: no_assistant_text`).
1017
+ Before this change the tracked envelope said `ok: true` with exit 0 while
1018
+ state.json said `ok: false`; call runs already failed this case and still do,
1019
+ now with the same `failureKind`. The second is a tracked or call child whose
1020
+ provider's last unrecovered error was a quota or 429 refusal
1021
+ (`provider_quota`). The third is a tracked run whose `--expect-file PATH`
1022
+ deliverable is missing (`deliverable_missing`). Scripts that treated exit 0
1023
+ from an empty tracked run as success will now see exit 1. Every failed or
1024
+ cancelled run carries `failureKind`, a closed enum listed in
1025
+ `docs/cli-reference.md`. A missing `--expect-file` deliverable is reported as
1026
+ `deliverable_missing` even when the run also produced no assistant text: the
1027
+ verdict the caller asked for outranks the no-output label, and both fail the
1028
+ run. Summaries from `runs`/`ps`/`wait` carry `failureKind` whenever the record
1029
+ holds the key, `null` included, so a consumer that derives its verdict from a
1030
+ summary agrees with the envelope instead of falling back to the older
1031
+ result-quality veto (a quiet work run whose changes landed succeeded).
1032
+ - **A work run that changed files but ended quietly still succeeds.** A
1033
+ tracked `work` or `followup` run that exits 0 without assistant text, while
1034
+ its work summary shows changed files or commits, is `succeeded` with a
1035
+ `no_assistant_text` warning, and the envelope carries the `workSummary` so an
1036
+ orchestrator can adopt the work. Without changes the same run fails as
1037
+ `no_assistant_text`. Safe and call runs are unaffected. Only persistent or
1038
+ attached worktrees record a work summary, so a quiet run in any other
1039
+ isolation still fails. Pass `--expect-file` for a stricter check.
1040
+ - Provider signals are classified from the final attempt only. On the tracked
1041
+ and call paths, quota, provider-error, and unrecovered-error verdicts read
1042
+ only the last attempt's own error events, last error message, and stderr, so
1043
+ an earlier attempt's 429 no longer labels a later attempt that failed for a
1044
+ different reason, and a final attempt's unrecovered quota error survives the
1045
+ retry merge. The final attempt owns the provider terminal state as well: an
1046
+ earlier attempt's `turn.cancelled`, max-turns stop, or refusal-coded failure
1047
+ no longer decides a merged run whose retry or failover answered with text and
1048
+ exited 0. The call path's empty-success retry now joins the thread-retry and
1049
+ auth-fallback merges, so a first attempt's quota line cannot classify a clean
1050
+ retry as `provider_quota`.
1051
+ - `wait` reports a running record whose runner process is gone (`staleReason`
1052
+ `dead_pid` or `missing_pid`) as `failureKind: runner_lost` instead of
1053
+ `stalled`. `stalled` is kept for the stall watchdog, which workflows retry as
1054
+ transient; a lost runner is not retried.
1055
+ - Workflows: a structured `agent()` call whose work child changed its worktree
1056
+ but returned no valid structured result is retried only by resuming the same
1057
+ session and asking for the structured result alone. When the engine cannot
1058
+ resume, Delegate does not launch a fresh child into that tree; the call fails
1059
+ as `structured_invalid` and the `agent_structured_exhausted` journal event
1060
+ carries the child's `workSummary`. Safe-mode children and work children
1061
+ without changes keep the existing correction retry.
1062
+ - Tracked `safe`/`work` launches accept a repeatable `--expect-file PATH`. After
1063
+ the child exits, Delegate checks the child's process group for members that
1064
+ are still alive and records an `orphanedProcesses` warning before it
1065
+ terminates them.
1066
+ - Call-mode JSON carries `assistantText`, `assistantTextChars`, and
1067
+ `assistantTextTruncated` on every engine, the same fields tracked envelopes
1068
+ use. `text`, `textChars`, and `textTruncated` stay for one release as
1069
+ deprecated aliases.
1070
+ - Workflows: `agent(..., on_failure="typed")` returns a falsy `AgentFailure`
1071
+ when structured retries run out. It carries the last parsed candidate, the
1072
+ attempt count, `failureKind`, the run id, and the served model and provider;
1073
+ the default is still `None`. The new globals are `agent_meta(label)`, which
1074
+ exposes the child's outcome and served model and provider, and
1075
+ `capabilities`, which lets a script detect these features. A failed child no
1076
+ longer erases an earlier attempt's parsed candidate. A child that exits 0
1077
+ without text now receives a structured correction retry. Structured parsing
1078
+ tolerates raw control characters, including inside a prose-wrapped or fenced
1079
+ answer: a candidate the scanner finds is decoded again with the stray
1080
+ characters stripped before a quoted fragment of the damaged document can be
1081
+ returned in its place.
1082
+ - Bundled Codex declarations move to the GPT-6 line. `gpt-6-sol` and
1083
+ `gpt-6-luna` replace the `gpt-5.6-sol`/`gpt-5.6-luna` rows and `gpt-5.6-terra`
1084
+ is dropped outright, verified against live `codex` enumeration on both the
1085
+ work and personal profiles (2026-09-22): Sol carries `low`-`ultra` at a
1086
+ `medium` default, Luna carries `low`-`max` at `medium`. The `pi` and `omp`
1087
+ bundled selectors follow to `openai-codex/gpt-6-sol`, and the Codex discovery
1088
+ fixture mirrors the live payload for the new slug. Doc prose that named
1089
+ `gpt-5.6-sol` as the single bundled `max`-capable Codex model was already
1090
+ stale and now names the bundled set (`gpt-6-astra`, `gpt-6-sol`,
1091
+ `gpt-6-luna`).
1092
+ - Bundled Claude declarations replace `claude-opus-5` with `claude-opus-5-5`,
1093
+ and the pinned-continuity error prose names the new id.
1094
+ - Bundled Grok declarations bumped to 4.7. The Grok CLI now ships `grok-4.7` as
1095
+ its upstream default (bundled rows: `grok-4.7`, `grok-4.6`). Cursor's 4.7
1096
+ ladder drops the `cursor-` prefix the 4.5/4.6 rows carried, so the bundled
1097
+ Cursor rows and the fixed-effort selector table now name `grok-4.7-xhigh` and
1098
+ `grok-4.7-xhigh-fast`; the still-served `cursor-grok-4.6-xhigh*` selectors
1099
+ stay in the fixed-effort table. The Grok discovery fixture mirrors the live
1100
+ `grok models` output, including `grok-4.7-build-fast`.
1101
+ - The skill-review preamble is explicitly bounded: read the served skill index
1102
+ once, read only the skills judged relevant, and do not run discovery CLIs to
1103
+ enumerate skills. The requirement stays mandatory; it no longer reads as an
1104
+ open-ended discovery task.
1105
+ - `workflow reject`'s `agent_rejected` event records `rejectedBy: "coordinator"`,
1106
+ and the workflow DSL `reject(key, reason, by=...)` accepts `seat`,
1107
+ `merge-gate`, or `coordinator`. Emitters that declare nothing keep the legacy
1108
+ event shape, so a missing `rejectedBy` means unknown, not seat failure.
1109
+ - A structured-output resume retry re-renders the target schema into the
1110
+ correction prompt instead of asking the model to recall the shape; both GLM
1111
+ review retries had rebuilt a root-array schema as an object wrapper and failed
1112
+ again.
1113
+
1114
+ ### Security
1115
+ - Native Codex and Claude session files now persist for every work run,
1116
+ because those runs are resumable by default. Codex's rollout under
1117
+ `CODEX_HOME/sessions` and Claude Code's transcript under its `projects`
1118
+ directory hold the prompt as sent, the child's replies, and tool inputs and
1119
+ results (file contents the child read, command output). They live in the
1120
+ harness's own store, outside `.delegate/`, outside Delegate's redaction, and
1121
+ outside `runs prune` and the worktree cleanup commands, and Delegate does
1122
+ not prune them. A profile with a separate `CODEX_HOME` or Claude config
1123
+ directory keeps them in that account's store. Reduce the footprint with
1124
+ `--no-resumable`, `codex.resumable: false`, or `claude.resumable: false`.
1125
+ The untrusted-input boundaries are unchanged: safe runs, call runs (Claude
1126
+ `call --pure` still passes `--no-session-persistence`), `--pass-through`
1127
+ runs, other engines, and workflow children without `resumable=True` never
1128
+ save a session.
1129
+ - **Work runs now have a write guard, on by default on Linux.** Work mode runs
1130
+ the child with the caller's own filesystem rights, so one confused lane (`rm
1131
+ -rf ~`, a stray redirect into a sibling checkout) could destroy things
1132
+ nobody can recreate. The guard is a protect-list, not a home allowlist: a
1133
+ named set of irreplaceable paths becomes read-only and everything else stays
1134
+ writable, and reads are never restricted. By default it protects, each only
1135
+ when it exists, the credential stores `~/.ssh`, `~/.gnupg`, `~/.config/gh`,
1136
+ `~/.config/gcloud`, `~/.aws`, `~/.azure`, `~/.kube`, `~/.netrc`,
1137
+ `~/.git-credentials`, and `~/.password-store`; `~/.ai-profiles`; the
1138
+ installed Delegate runtime (`~/.local/bin/delegate` plus `~/.delegate/src`,
1139
+ `releases`, `bin`, and `config*.json`); and the code root (`~/Code`), so a
1140
+ lane in one checkout cannot write into a sibling. Inside those paths the
1141
+ run's own needs are re-opened: the execution root, the git common directory,
1142
+ the run registry, run scratch and compact temp, mail-push homes, and the
1143
+ selected engine's home. `/tmp`, `TMPDIR`, and home caches such as `~/.cache`
1144
+ are not protected. The guard never applies to safe mode, `--pass-through`
1145
+ runs are not guarded, and it does not stop reads, network use, or use of
1146
+ credentials already in the environment.
1147
+ - Backends and platform defaults for the write guard. On Linux the child runs
1148
+ under bubblewrap (`bwrap --dev-bind / /` with each protected path bound
1149
+ read-only), preflighted before launch; the execution root is bound as a
1150
+ mount of its own so a lane cannot rename its checkout away, a new hard link
1151
+ to a protected file cannot be made, and writes through symlinks to protected
1152
+ paths are refused. **On macOS the Seatbelt guard is opt-in:** with
1153
+ `isolation.writeGuard.macosSeatbelt` at its default `false`, the guard
1154
+ status is `off` with no warning and no refusal, and setting it `true` opts
1155
+ in to the `sandbox-exec` guard, which then wraps every engine except a Codex
1156
+ lane whose own `workspace-write` sandbox is on (a Codex lane with its sandbox
1157
+ bypassed is wrapped, and that combination is not yet live-tested). To keep the `external-sandbox` policy
1158
+ profile but put Codex's own sandbox back for work lanes, set
1159
+ `policy.harness.codex.work.bypassApprovalsAndSandbox` to `false`; Delegate
1160
+ then emits `--sandbox workspace-write` with `--add-dir` roots for the git
1161
+ common directory, registry, scratch, temp, `--writable` paths, and existing
1162
+ home caches.
1163
+ - Configure and inspect the write guard with `isolation.writeGuard`
1164
+ (`enabled`, `onUnavailable`, `macosSeatbelt`, `codeRoot`, `add`, `remove`,
1165
+ `writable`, `homeCaches`; an unknown key or wrong type fails as
1166
+ `invalid_isolation_config`), the `DELEGATE_WRITE_GUARD` environment variable
1167
+ (`off`, `0`, `false`, or `no` disables, `on`, `1`, `true`, or `yes` enables,
1168
+ and it overrides the config), and `--writable PATH` (repeatable, `work`
1169
+ mode, CLI-only), which re-opens an existing path for one run. The plan
1170
+ (backend, protected paths, and each re-open with its reason) is recorded in
1171
+ the run manifest under `writeGuard` and printed by `--dry-run`, and
1172
+ work-mode prompts gain a two-sentence note naming the protected paths so a
1173
+ lane reports a blocked write instead of working around it. When no backend
1174
+ is usable, `isolation.writeGuard.onUnavailable` decides: `warn` (the
1175
+ default) launches unguarded and records a warning, and `refuse` fails the
1176
+ launch with `write_guard_unavailable`. On Linux with `warn`, a bwrap
1177
+ preflight that fails on one unbindable path drops only that path (listed
1178
+ under `writeGuard.unbound`, with a warning) instead of the whole guard; when
1179
+ a protected path was dropped the status is `partial` rather than `enforced`
1180
+ and the warning starts "work write guard is PARTIAL". `refuse` never
1181
+ retries.
1182
+ - Only the selected engine's own home variable (`CODEX_HOME`,
1183
+ `CLAUDE_CONFIG_DIR`, `KIMI_CODE_HOME`, or that engine's default directory)
1184
+ can re-open a directory inside a protected path, and only when it is
1185
+ provably one profile. The home must itself carry an identity file
1186
+ (`.claude.json`, `.credentials.json`, or `auth.json` beside `config.toml`),
1187
+ and a bounded scan below it (three levels, no symlinks, at most 2000
1188
+ directories) must finish and find no other profile's home. A parent of
1189
+ profiles, a home that is itself protected, and a scan that hits the cap or
1190
+ cannot list a directory are refused: the directory stays read-only, the
1191
+ manifest lists it under `writeGuard.refused` with the reason, and the run
1192
+ gets a warning. An engine Delegate has no home variable for (droid, for one)
1193
+ gets no automatic re-open; name its profile directory in
1194
+ `isolation.writeGuard.writable` or pass `--writable`. Known limits: a second
1195
+ profile nested deeper than three levels is not found; a hard link that
1196
+ already exists between a protected file and a writable name is the same
1197
+ inode, so a write through it changes the protected file; and a protected
1198
+ path that is itself a symlink is protected at its target while the link
1199
+ stays replaceable.
1200
+ - A lane started through an estate launcher (`estate-claude`, `estate-codex`,
1201
+ `estate-omp`, any program named `estate-*`) gets the profiles root
1202
+ (`ESTATE_AI_PROFILES_ROOT`, then `AI_PROFILES_ROOT`, then `~/.ai-profiles`)
1203
+ re-opened, recorded as "estate launcher writes profile state". The launcher
1204
+ picks an account, refreshes its token, and writes session, plugin, and lock
1205
+ state there on every launch, so protecting it stopped `estate-claude` before
1206
+ Claude started. Those lanes can therefore write every profile's files; lanes
1207
+ started any other way keep `~/.ai-profiles` protected, and a Codex lane with
1208
+ its own sandbox on never gets it as a writable root.
1209
+
8
1210
  ## [0.31.0] - 2026-09-14
9
1211
 
10
1212
  ### Harness compatibility audit (2026-09-07)
@@ -1387,7 +2589,9 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
1387
2589
 
1388
2590
  - Releases before 0.1.3 predate this changelog.
1389
2591
 
1390
- [Unreleased]: https://github.com/treygoff24/delegate-agent/compare/v0.31.0...HEAD
2592
+ [Unreleased]: https://github.com/treygoff24/delegate-agent/compare/v0.33.0...HEAD
2593
+ [0.33.0]: https://github.com/treygoff24/delegate-agent/compare/v0.32.0...v0.33.0
2594
+ [0.32.0]: https://github.com/treygoff24/delegate-agent/compare/v0.31.0...v0.32.0
1391
2595
  [0.31.0]: https://github.com/treygoff24/delegate-agent/compare/v0.29.1...v0.31.0
1392
2596
  [0.30.0]: https://github.com/treygoff24/delegate-agent/compare/v0.29.1...v0.30.0
1393
2597
  [0.29.1]: https://github.com/treygoff24/delegate-agent/compare/v0.29.0...v0.29.1