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