multi-forge 0.8.0__tar.gz → 0.8.1__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 (417) hide show
  1. {multi_forge-0.8.0 → multi_forge-0.8.1}/PKG-INFO +1 -1
  2. {multi_forge-0.8.0 → multi_forge-0.8.1}/pyproject.toml +1 -1
  3. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/commands.py +1 -1
  4. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/direct_commands.py +2 -2
  5. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/policy.py +2 -2
  6. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory.py +118 -37
  7. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/policy.py +2 -3
  8. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_lane.py +4 -2
  9. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/__init__.py +3 -2
  10. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/__init__.py +1 -0
  11. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/io.py +28 -3
  12. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/codex_hooks.py +2 -2
  13. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/supervisor.py +11 -12
  14. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/consumer_lanes.py +11 -8
  15. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/overrides.py +3 -2
  16. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/passport.py +232 -16
  17. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/project_memory.py +15 -2
  18. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/store.py +2 -1
  19. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/16-memory.md +81 -2
  20. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist.md +8 -6
  21. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/resources/checklist.md +100 -5
  22. {multi_forge-0.8.0 → multi_forge-0.8.1}/.gitignore +0 -0
  23. {multi_forge-0.8.0 → multi_forge-0.8.1}/LICENSE +0 -0
  24. {multi_forge-0.8.0 → multi_forge-0.8.1}/NOTICE +0 -0
  25. {multi_forge-0.8.0 → multi_forge-0.8.1}/README.md +0 -0
  26. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/agents/.gitkeep +0 -0
  27. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/commands/.gitkeep +0 -0
  28. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/__init__.py +0 -0
  29. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/__init__.py +0 -0
  30. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/adapters/__init__.py +0 -0
  31. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/adapters/litellm.py +0 -0
  32. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/creation.py +0 -0
  33. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/registry.py +0 -0
  34. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/__init__.py +0 -0
  35. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/base.py +0 -0
  36. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/remote/openrouter.py +0 -0
  37. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/backend/sources.py +0 -0
  38. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/__init__.py +0 -0
  39. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/activity.py +0 -0
  40. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/auth.py +0 -0
  41. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/backend.py +0 -0
  42. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/claude.py +0 -0
  43. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/codex.py +0 -0
  44. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/config_cmd.py +0 -0
  45. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/consumer_lane_freeze.py +0 -0
  46. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/editor.py +0 -0
  47. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/extensions.py +0 -0
  48. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/gc.py +0 -0
  49. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/guards.py +0 -0
  50. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/__init__.py +0 -0
  51. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/_group.py +0 -0
  52. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/_helpers.py +0 -0
  53. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_patch.py +0 -0
  54. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_policy.py +0 -0
  55. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
  56. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/protocols.py +0 -0
  57. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
  58. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/hooks/verification.py +0 -0
  59. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/logs.py +0 -0
  60. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/main.py +0 -0
  61. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory_report.py +0 -0
  62. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/memory_writer.py +0 -0
  63. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/model.py +0 -0
  64. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/output.py +0 -0
  65. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy.py +0 -0
  66. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy_audit.py +0 -0
  67. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/proxy_costs.py +0 -0
  68. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/runtime.py +0 -0
  69. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/search.py +0 -0
  70. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session.py +0 -0
  71. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_codex.py +0 -0
  72. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_fork.py +0 -0
  73. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_lifecycle.py +0 -0
  74. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_manage.py +0 -0
  75. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_memory.py +0 -0
  76. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_model_pin.py +0 -0
  77. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_resume_modes.py +0 -0
  78. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_rewind.py +0 -0
  79. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_routing.py +0 -0
  80. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/session_supervisor_options.py +0 -0
  81. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/status_line.py +0 -0
  82. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/__init__.py +0 -0
  83. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/context.py +0 -0
  84. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/names.py +0 -0
  85. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/palette.py +0 -0
  86. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/registry.py +0 -0
  87. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/statusline/throttle.py +0 -0
  88. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/telemetry.py +0 -0
  89. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/trace.py +0 -0
  90. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/transfer.py +0 -0
  91. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/cli/workflow.py +0 -0
  92. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/__init__.py +0 -0
  93. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/dataclass_utils.py +0 -0
  94. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/__init__.py +0 -0
  95. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/backends/__init__.py +0 -0
  96. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/backends/litellm.yaml +0 -0
  97. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/__init__.py +0 -0
  98. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -0
  99. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +0 -0
  100. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +0 -0
  101. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +0 -0
  102. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
  103. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
  104. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
  105. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -0
  106. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
  107. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
  108. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
  109. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +0 -0
  110. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
  111. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
  112. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +0 -0
  113. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
  114. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
  115. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
  116. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
  117. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
  118. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
  119. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/loader.py +0 -0
  120. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/config/schema.py +0 -0
  121. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/__init__.py +0 -0
  122. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/__init__.py +0 -0
  123. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/capabilities.py +0 -0
  124. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/credentials_file.py +0 -0
  125. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/protocols.py +0 -0
  126. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/secrets.py +0 -0
  127. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/auth/template_secrets.py +0 -0
  128. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/backend_dependency.py +0 -0
  129. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/credential_registry.py +0 -0
  130. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/__init__.py +0 -0
  131. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/model_catalog.yaml +0 -0
  132. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
  133. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
  134. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
  135. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/effort.py +0 -0
  136. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/__init__.py +0 -0
  137. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/_lifecycle.py +0 -0
  138. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/claude.py +0 -0
  139. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/codex.py +0 -0
  140. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/codex_stream.py +0 -0
  141. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/invoker/types.py +0 -0
  142. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/lanes.py +0 -0
  143. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/base.py +0 -0
  144. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/litellm.py +0 -0
  145. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/openai_compat.py +0 -0
  146. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/clients/openrouter.py +0 -0
  147. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/credentials.py +0 -0
  148. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/detection.py +0 -0
  149. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/errors.py +0 -0
  150. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/protocols.py +0 -0
  151. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/llm/types.py +0 -0
  152. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/logging.py +0 -0
  153. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/__init__.py +0 -0
  154. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/catalog.py +0 -0
  155. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/direct_model.py +0 -0
  156. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/models/types.py +0 -0
  157. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/naming.py +0 -0
  158. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/__init__.py +0 -0
  159. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/backend_reconcile.py +0 -0
  160. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/claude_session.py +0 -0
  161. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_bridge.py +0 -0
  162. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_enrollment.py +0 -0
  163. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_interactive.py +0 -0
  164. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/codex_session.py +0 -0
  165. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/context.py +0 -0
  166. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/gc.py +0 -0
  167. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/policy.py +0 -0
  168. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/provider_trace.py +0 -0
  169. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/proxy.py +0 -0
  170. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/resolution.py +0 -0
  171. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/session.py +0 -0
  172. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/session_context.py +0 -0
  173. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/transfer.py +0 -0
  174. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/ops/usage_summary.py +0 -0
  175. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/paths.py +0 -0
  176. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/process.py +0 -0
  177. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/provider_types.py +0 -0
  178. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/__init__.py +0 -0
  179. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/cost_tracking.py +0 -0
  180. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/env.py +0 -0
  181. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/headless_json.py +0 -0
  182. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/proxy.py +0 -0
  183. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/routing.py +0 -0
  184. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/session_runner.py +0 -0
  185. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/structured_output.py +0 -0
  186. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/tagger.py +0 -0
  187. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/reactive/throttle.py +0 -0
  188. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/run_id.py +0 -0
  189. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/__init__.py +0 -0
  190. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_preflight.py +0 -0
  191. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_preflight_cache.py +0 -0
  192. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
  193. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime/registry.py +0 -0
  194. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/runtime_vocab.py +0 -0
  195. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/__init__.py +0 -0
  196. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/exceptions.py +0 -0
  197. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/lock.py +0 -0
  198. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/retention.py +0 -0
  199. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/timestamps.py +0 -0
  200. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/state/versioned_store.py +0 -0
  201. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/__init__.py +0 -0
  202. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/caps.py +0 -0
  203. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/downstream.py +0 -0
  204. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/jsonl_io.py +0 -0
  205. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/upstream.py +0 -0
  206. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/telemetry/vocabulary.py +0 -0
  207. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/tiers.py +0 -0
  208. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/transcript.py +0 -0
  209. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/typing_helpers.py +0 -0
  210. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/__init__.py +0 -0
  211. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/billing.py +0 -0
  212. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/correlation.py +0 -0
  213. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/emit.py +0 -0
  214. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/ledger.py +0 -0
  215. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/measurement.py +0 -0
  216. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/usage/vocabulary.py +0 -0
  217. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/__init__.py +0 -0
  218. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/queue.py +0 -0
  219. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/core/workqueue/types.py +0 -0
  220. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/__init__.py +0 -0
  221. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/cli.py +0 -0
  222. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/doctor.py +0 -0
  223. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/exceptions.py +0 -0
  224. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hook_dispatcher.py +0 -0
  225. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hook_migration.py +0 -0
  226. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/hooks.py +0 -0
  227. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/installer.py +0 -0
  228. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/models.py +0 -0
  229. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/preset.py +0 -0
  230. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/project_compat.py +0 -0
  231. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/project_registry.py +0 -0
  232. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/settings_merge.py +0 -0
  233. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/tracking.py +0 -0
  234. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/install/version.py +0 -0
  235. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/__init__.py +0 -0
  236. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/__init__.py +0 -0
  237. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/base.py +0 -0
  238. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
  239. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/registry.py +0 -0
  240. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/deterministic/tdd.py +0 -0
  241. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/engine.py +0 -0
  242. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/protocols.py +0 -0
  243. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/queries.py +0 -0
  244. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/__init__.py +0 -0
  245. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/plan_check.py +0 -0
  246. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/shadow.py +0 -0
  247. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/shadow_runner.py +0 -0
  248. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/semantic/verdict.py +0 -0
  249. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/store.py +0 -0
  250. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/supervisor_lane_degrade.py +0 -0
  251. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/__init__.py +0 -0
  252. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/config.py +0 -0
  253. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/handlers.py +0 -0
  254. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/team/prompts.py +0 -0
  255. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/types.py +0 -0
  256. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/__init__.py +0 -0
  257. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/branches.py +0 -0
  258. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/config.py +0 -0
  259. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/divergence.py +0 -0
  260. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/policy.py +0 -0
  261. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/policy/workflow/stages.py +0 -0
  262. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/__init__.py +0 -0
  263. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/audit_logger.py +0 -0
  264. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/base_client.py +0 -0
  265. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/client_adapter.py +0 -0
  266. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/client_factory.py +0 -0
  267. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/converters.py +0 -0
  268. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/cost_logger.py +0 -0
  269. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/cost_tracker.py +0 -0
  270. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/data_models.py +0 -0
  271. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/error_hints.py +0 -0
  272. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/intercept.py +0 -0
  273. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/metrics.py +0 -0
  274. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/model_spec.py +0 -0
  275. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/passthrough.py +0 -0
  276. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/ports.py +0 -0
  277. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/provider_trace_logger.py +0 -0
  278. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxies.py +0 -0
  279. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_identity.py +0 -0
  280. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_orchestrator.py +0 -0
  281. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/proxy_startup.py +0 -0
  282. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/responses_ingress.py +0 -0
  283. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/responses_passthrough.py +0 -0
  284. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/server.py +0 -0
  285. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/stream_relay.py +0 -0
  286. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/proxy/utils.py +0 -0
  287. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/__init__.py +0 -0
  288. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/adversarial.py +0 -0
  289. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/consensus.py +0 -0
  290. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/engine.py +0 -0
  291. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/models.py +0 -0
  292. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/__init__.py +0 -0
  293. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
  294. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
  295. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-performance.md +0 -0
  296. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-quick.md +0 -0
  297. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview-security.md +0 -0
  298. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/codereview.md +0 -0
  299. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
  300. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/debate_evaluation.md +0 -0
  301. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/docreview-quick.md +0 -0
  302. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/docreview.md +0 -0
  303. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/resources/thinkdeep.md +0 -0
  304. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/routing.py +0 -0
  305. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/review/synthesis.py +0 -0
  306. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/runtime_config.py +0 -0
  307. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/__init__.py +0 -0
  308. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/bm25_store.py +0 -0
  309. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/content_store.py +0 -0
  310. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/engine.py +0 -0
  311. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/exceptions.py +0 -0
  312. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/extractor.py +0 -0
  313. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/index_state.py +0 -0
  314. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/store.py +0 -0
  315. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/search/tokenizer.py +0 -0
  316. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/__init__.py +0 -0
  317. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/active.py +0 -0
  318. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/addendum.py +0 -0
  319. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/artifacts.py +0 -0
  320. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/__init__.py +0 -0
  321. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/cleanup.py +0 -0
  322. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/invoke.py +0 -0
  323. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/paths.py +0 -0
  324. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/claude/relocate.py +0 -0
  325. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/cleanup.py +0 -0
  326. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/codex_handoff.py +0 -0
  327. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/codex_invoke.py +0 -0
  328. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/config.py +0 -0
  329. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/context_limit.py +0 -0
  330. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/effective.py +0 -0
  331. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/exceptions.py +0 -0
  332. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/__init__.py +0 -0
  333. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/models.py +0 -0
  334. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/hooks/session_start.py +0 -0
  335. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/identity.py +0 -0
  336. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/index.py +0 -0
  337. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/launch.py +0 -0
  338. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/launch_confirmation.py +0 -0
  339. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/manager.py +0 -0
  340. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/memory_inheritance.py +0 -0
  341. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/memory_writer.py +0 -0
  342. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/model_pin.py +0 -0
  343. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/models.py +0 -0
  344. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/plan_resolution.py +0 -0
  345. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/prev_sessions.py +0 -0
  346. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/rewind.py +0 -0
  347. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/shadow_curation.py +0 -0
  348. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/transfer.py +0 -0
  349. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/validation.py +0 -0
  350. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/__init__.py +0 -0
  351. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/cleanup.py +0 -0
  352. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/config_copy.py +0 -0
  353. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/session/worktree/create.py +0 -0
  354. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/__init__.py +0 -0
  355. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/container.py +0 -0
  356. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/forge/sidecar/docker.py +0 -0
  357. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/analyze/SKILL.md +0 -0
  358. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/challenge/SKILL.md +0 -0
  359. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/consensus/SKILL.md +0 -0
  360. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/consensus/resources/synthesis.md +0 -0
  361. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/debate/SKILL.md +0 -0
  362. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/panel/SKILL.md +0 -0
  363. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/panel/resources/synthesis.md +0 -0
  364. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/SKILL.md +0 -0
  365. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/0-enable.md +0 -0
  366. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
  367. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/10-resume.md +0 -0
  368. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/11-config.md +0 -0
  369. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/12-search.md +0 -0
  370. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/13-policy.md +0 -0
  371. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
  372. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/15-skills.md +0 -0
  373. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/17-info.md +0 -0
  374. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/18-disable.md +0 -0
  375. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
  376. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/2-extension.md +0 -0
  377. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
  378. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
  379. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/4-proxy.md +0 -0
  380. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/5-session.md +0 -0
  381. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/6-hook.md +0 -0
  382. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/7-costs.md +0 -0
  383. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
  384. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
  385. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/resources/report-template.md +0 -0
  386. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/scripts/start-container.sh +0 -0
  387. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/qa/scripts/walkthrough-state.py +0 -0
  388. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/SKILL.md +0 -0
  389. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/claude-4.6.md +0 -0
  390. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/claude-4.8.md +0 -0
  391. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/gemini-3.1.md +0 -0
  392. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/gpt-5.5.md +0 -0
  393. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/references/skills-writing-guide.md +0 -0
  394. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-anthropic.md +0 -0
  395. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-gemini.md +0 -0
  396. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code-openai.md +0 -0
  397. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review/resources/code.md +0 -0
  398. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/SKILL.md +0 -0
  399. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
  400. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
  401. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
  402. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/review-docs/resources/docs.md +0 -0
  403. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/smoke-test/SKILL.md +0 -0
  404. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
  405. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/SKILL.md +0 -0
  406. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-anthropic.md +0 -0
  407. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-gemini.md +0 -0
  408. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code-openai.md +0 -0
  409. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/code.md +0 -0
  410. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
  411. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-gemini.md +0 -0
  412. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs-openai.md +0 -0
  413. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/understand/resources/docs.md +0 -0
  414. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/SKILL.md +0 -0
  415. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
  416. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
  417. {multi_forge-0.8.0 → multi_forge-0.8.1}/src/skills/walkthrough/scripts/walkthrough-state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multi-forge
3
- Version: 0.8.0
3
+ Version: 0.8.1
4
4
  Summary: Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration
5
5
  Project-URL: Homepage, https://github.com/hapa1i/multi-forge
6
6
  Project-URL: Repository, https://github.com/hapa1i/multi-forge
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "multi-forge"
3
- version = "0.8.0"
3
+ version = "0.8.1"
4
4
  description = "Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1016,7 +1016,7 @@ def worktree_create() -> None:
1016
1016
  # Fallback: try without -b (use detached HEAD)
1017
1017
  logger.debug("worktree-create: branched creation failed: %s", result.stderr.strip())
1018
1018
  result = subprocess.run(
1019
- [git, "worktree", "add", str(worktree_path)],
1019
+ [git, "worktree", "add", "--detach", str(worktree_path)],
1020
1020
  cwd=str(repo_root),
1021
1021
  capture_output=True,
1022
1022
  text=True,
@@ -770,8 +770,8 @@ def _handle_policy_supervisor(argv: list[str]) -> None:
770
770
  - ``%policy supervisor``: show current config (including the bound consumer lane)
771
771
 
772
772
  The consumer-lane *runtime* is intentionally not settable here -- it is a resolving-command
773
- flag (``forge policy supervisor set --runtime``) so the binding stays write-once. This path
774
- only shows the bound lane.
773
+ flag (``forge policy supervisor set <target> --runtime``) so the binding stays write-once.
774
+ This path only shows the bound lane.
775
775
  """
776
776
  cwd = Path.cwd().resolve()
777
777
  store = resolve_session_store(cwd)
@@ -172,8 +172,8 @@ def register_supervisor_and_restore(engine: Any, effective: Any, manifest: Any)
172
172
 
173
173
  Returns the supervisor's injected consumer-lane binding (None when no supervisor is
174
174
  configured, or when it resolves to the default lane). The caller threads this same
175
- value into the post-eval freeze so the binding records the lane that actually
176
- dispatched -- not a fresh manifest read that could race a concurrent intent change.
175
+ value into the post-eval freeze so the binding records the lane committed for this
176
+ registered check -- not a fresh manifest read that could race an intent change.
177
177
  """
178
178
  sup = effective.policy.supervisor if effective.policy else None
179
179
  has_supervisor = bool(sup and sup.resume_id and not sup.suspended)
@@ -30,12 +30,17 @@ from forge.session.exceptions import (
30
30
  from forge.session.passport import (
31
31
  VALID_STRATEGY_NAMES,
32
32
  Passport,
33
+ PreparedPassportWrite,
34
+ apply_prepared_passport_write,
33
35
  derive_shadow_path,
36
+ prepare_passport_write,
34
37
  read_passport,
35
38
  remove_passport,
36
39
  resolve_passport_source,
37
40
  resolve_with_overrides,
38
41
  synthesize_passport,
42
+ upgrade_passport_envelope,
43
+ validate_okf_reserved_basenames,
39
44
  write_passport,
40
45
  )
41
46
  from forge.session.project_memory import (
@@ -87,6 +92,7 @@ def memory() -> None:
87
92
  forge memory track docs/changelog.md --strategy changelog # author a passport (sessionless)
88
93
  forge memory list # show passported docs
89
94
  forge memory passport show docs/changelog.md # inspect a passport
95
+ forge memory passport upgrade docs/changelog.md # add the OKF envelope
90
96
  forge memory shadows review --for docs/impl_notes.md # curate shadow proposals
91
97
  """
92
98
 
@@ -187,6 +193,9 @@ def track_cmd(
187
193
  except PassportError as e:
188
194
  raise click.ClickException(f"Malformed passport in {path}: {e}") from e
189
195
 
196
+ if passport is None and intent is not None and not intent.strip():
197
+ raise click.ClickException("--intent must be a non-empty string when creating a passport.")
198
+
190
199
  # Scan roots power the out-of-root warning and the collision check. A
191
200
  # corrupt config must not block authoring (system-boundary warn+degrade).
192
201
  roots = _resolve_scan_roots()
@@ -233,9 +242,9 @@ def track_cmd(
233
242
  new_pp = synthesize_passport(
234
243
  strategy=strategy, # type: ignore[arg-type] # checked above
235
244
  intent=intent,
236
- writers=writers or "all-sessions",
245
+ writers=writers if writers is not None else "all-sessions",
237
246
  )
238
- write_passport(abs_path, new_pp)
247
+ write_passport(abs_path, new_pp, okf_path=path)
239
248
  except PassportError as e:
240
249
  raise click.ClickException(str(e)) from e
241
250
  console.print(f"Passport created for [cyan]{path}[/cyan] (strategy: {strategy}).")
@@ -300,17 +309,16 @@ def _track_existing_shadow_only(
300
309
  )
301
310
 
302
311
  has_flags = strategy is not None or writers is not None
303
- updated = False
304
- if has_flags:
305
- try:
312
+ resolved_pp = passport
313
+ pp_warnings: list[str] = []
314
+ prepared: PreparedPassportWrite | None = None
315
+ try:
316
+ if has_flags:
306
317
  resolved_pp, pp_warnings = resolve_with_overrides(passport, strategy=strategy, writers=writers)
307
- except PassportError as exc:
308
- raise click.ClickException(str(exc)) from exc
309
- if pp_warnings:
310
- write_passport(abs_path, resolved_pp)
311
- updated = True
312
- for w in pp_warnings:
313
- console.print(f"[yellow]Warning:[/yellow] {w}")
318
+ if pp_warnings:
319
+ prepared = prepare_passport_write(abs_path, resolved_pp)
320
+ except PassportError as exc:
321
+ raise click.ClickException(str(exc)) from exc
314
322
 
315
323
  created = _auto_create_shadow(shadow_path, forge_root)
316
324
  shadow_abs = (forge_root / shadow_path).resolve()
@@ -318,7 +326,11 @@ def _track_existing_shadow_only(
318
326
  raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
319
327
  if created:
320
328
  console.print(f"Shadow file created: {shadow_path}.")
321
- if updated:
329
+ if pp_warnings:
330
+ assert prepared is not None
331
+ apply_prepared_passport_write(abs_path, prepared)
332
+ for w in pp_warnings:
333
+ console.print(f"[yellow]Warning:[/yellow] {w}")
322
334
  console.print(f"Passport updated in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
323
335
  else:
324
336
  console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
@@ -353,6 +365,10 @@ def _track_propose(
353
365
  # Self-shadow: compare resolved paths, not raw strings
354
366
  resolved_shadow = (forge_root / shadow_path).resolve()
355
367
  resolved_official = (forge_root / path).resolve()
368
+ try:
369
+ validate_okf_reserved_basenames(shadow_path, resolved_shadow)
370
+ except PassportError as exc:
371
+ raise click.ClickException(f"Invalid shadow path: {exc}") from exc
356
372
  if resolved_shadow == resolved_official:
357
373
  raise click.ClickException("Shadow path cannot be the same as the official doc.")
358
374
 
@@ -360,17 +376,15 @@ def _track_propose(
360
376
  if collision:
361
377
  raise click.ClickException(collision)
362
378
 
363
- # Auto-create shadow file if Forge-owned
364
- created = _auto_create_shadow(shadow_path, forge_root)
365
- shadow_abs = (forge_root / shadow_path).resolve()
366
- if not shadow_abs.is_file():
367
- raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
368
-
369
- # Passport handling: synthesize or update.
379
+ # Prepare the complete effective passport before materializing the shadow.
380
+ # For existing passports this deliberately omits ``okf_path`` so re-track
381
+ # never validates, repairs, or adds an outer envelope.
370
382
  # For existing passports, pass strategy only when the user explicitly provided --strategy
371
383
  # so the passport's own strategy is preserved by default.
384
+ pp_warnings: list[str]
385
+ prepared: PreparedPassportWrite | None = None
386
+ result_kind: str
372
387
  if isinstance(passport, Passport) and passport.update.mode == "shadow-only":
373
- # Already shadow-only -- apply overrides if any
374
388
  try:
375
389
  resolved_pp, pp_warnings = resolve_with_overrides(
376
390
  passport,
@@ -378,17 +392,12 @@ def _track_propose(
378
392
  shadow_path=(shadow_path if shadow_path != passport.update.shadow_path else None),
379
393
  writers=writers,
380
394
  )
395
+ if pp_warnings:
396
+ prepared = prepare_passport_write(abs_path, resolved_pp)
381
397
  except PassportError as e:
382
398
  raise click.ClickException(str(e)) from e
383
- if pp_warnings:
384
- write_passport(abs_path, resolved_pp)
385
- for w in pp_warnings:
386
- console.print(f"[yellow]Warning:[/yellow] {w}")
387
- console.print(f"Passport updated in [cyan]{path}[/cyan]. Future sessions will use the new values.")
388
- else:
389
- console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
399
+ result_kind = "updated" if pp_warnings else "unchanged"
390
400
  elif isinstance(passport, Passport):
391
- # Convert existing direct passport to shadow-only
392
401
  try:
393
402
  resolved_pp, pp_warnings = resolve_with_overrides(
394
403
  passport,
@@ -397,25 +406,45 @@ def _track_propose(
397
406
  shadow_path=shadow_path,
398
407
  writers=writers,
399
408
  )
409
+ prepared = prepare_passport_write(abs_path, resolved_pp)
400
410
  except PassportError as e:
401
411
  raise click.ClickException(str(e)) from e
402
- write_passport(abs_path, resolved_pp)
403
- for w in pp_warnings:
404
- console.print(f"[yellow]Warning:[/yellow] {w}")
405
- console.print(f"Passport in [cyan]{path}[/cyan] converted to shadow-only proposals at {shadow_path}.")
412
+ result_kind = "converted"
406
413
  else:
407
- # No passport -- synthesize with default strategy
408
414
  try:
409
415
  new_pp = synthesize_passport(
410
416
  strategy=new_passport_strategy,
411
417
  intent=intent,
412
418
  update_mode="shadow-only",
413
419
  shadow_path=shadow_path,
414
- writers=writers or "all-sessions",
420
+ writers=writers if writers is not None else "all-sessions",
415
421
  )
416
- write_passport(abs_path, new_pp)
422
+ prepared = prepare_passport_write(abs_path, new_pp, okf_path=path)
417
423
  except PassportError as e:
418
424
  raise click.ClickException(str(e)) from e
425
+ pp_warnings = []
426
+ result_kind = "created"
427
+
428
+ # Materialization begins only after path, collision, effective-passport,
429
+ # frontmatter, and (for new tracking) OKF envelope validation all succeed.
430
+ created = _auto_create_shadow(shadow_path, forge_root)
431
+ shadow_abs = (forge_root / shadow_path).resolve()
432
+ if not shadow_abs.is_file():
433
+ raise click.ClickException(f"Shadow file does not exist: {shadow_path}")
434
+
435
+ if result_kind != "unchanged":
436
+ assert prepared is not None
437
+ apply_prepared_passport_write(abs_path, prepared)
438
+
439
+ for w in pp_warnings:
440
+ console.print(f"[yellow]Warning:[/yellow] {w}")
441
+ if result_kind == "updated":
442
+ console.print(f"Passport updated in [cyan]{path}[/cyan]. Future sessions will use the new values.")
443
+ elif result_kind == "unchanged":
444
+ console.print(f"Passport already present in [cyan]{path}[/cyan] (shadow-only proposals at {shadow_path}).")
445
+ elif result_kind == "converted":
446
+ console.print(f"Passport in [cyan]{path}[/cyan] converted to shadow-only proposals at {shadow_path}.")
447
+ else:
419
448
  console.print(
420
449
  f"Shadow-only passport written for [cyan]{path}[/cyan] "
421
450
  f"(strategy: {new_passport_strategy}, proposals at {shadow_path})."
@@ -1029,7 +1058,7 @@ def _review_curate(
1029
1058
 
1030
1059
  @memory.group("passport")
1031
1060
  def passport_group() -> None:
1032
- """Inspect and remove memory-doc passports."""
1061
+ """Inspect, upgrade, and remove memory-doc passports."""
1033
1062
 
1034
1063
 
1035
1064
  @passport_group.command("show")
@@ -1115,6 +1144,58 @@ def passport_show_cmd(path: str, as_json: bool) -> None:
1115
1144
  console.print(table)
1116
1145
 
1117
1146
 
1147
+ @passport_group.command("upgrade")
1148
+ @click.argument("path")
1149
+ def passport_upgrade_cmd(path: str) -> None:
1150
+ """Add missing OKF envelope fields to an existing passport."""
1151
+ try:
1152
+ ctx = ExecutionContext.from_cwd()
1153
+ except ForgeOpError as e:
1154
+ print_error(str(e), console=err_console)
1155
+ print_tip("Run this command from an enabled Forge project.", console=err_console)
1156
+ sys.exit(1)
1157
+
1158
+ if ctx.forge_root is None:
1159
+ print_error("Not inside a Forge project.", console=err_console)
1160
+ print_tip("Run 'forge extension enable' first.", console=err_console)
1161
+ sys.exit(1)
1162
+
1163
+ from forge.cli.guards import enforce_target_project_compatibility
1164
+
1165
+ enforce_target_project_compatibility(ctx.forge_root)
1166
+
1167
+ resolved_base = ctx.forge_root.resolve()
1168
+ reason = is_safe_designated_doc_path(path, ctx.forge_root, resolved_base)
1169
+ if reason:
1170
+ print_error(f"Invalid path: {reason}", console=err_console)
1171
+ print_tip(
1172
+ "Use a project-relative Markdown path inside this Forge project.",
1173
+ console=err_console,
1174
+ )
1175
+ sys.exit(1)
1176
+
1177
+ abs_path = (ctx.forge_root / path).resolve()
1178
+ if not abs_path.is_file():
1179
+ print_error(f"File not found: {path}", console=err_console)
1180
+ print_tip("Check the project-relative path and try again.", console=err_console)
1181
+ sys.exit(1)
1182
+
1183
+ try:
1184
+ added = upgrade_passport_envelope(abs_path, logical_path=path)
1185
+ except PassportError as e:
1186
+ print_error(f"Could not upgrade passport in {path}: {e}", console=err_console)
1187
+ print_tip(
1188
+ "Fix the reported frontmatter or path issue, then rerun this command.",
1189
+ console=err_console,
1190
+ )
1191
+ sys.exit(1)
1192
+
1193
+ if added:
1194
+ console.print(f"OKF envelope upgraded in [cyan]{path}[/cyan] (added: {', '.join(added)}).")
1195
+ else:
1196
+ console.print(f"OKF envelope already complete in [cyan]{path}[/cyan]; no changes made.")
1197
+
1198
+
1118
1199
  @passport_group.command("remove")
1119
1200
  @click.argument("path")
1120
1201
  @click.option("--json", "as_json", is_flag=True, default=False, help="Output as JSON")
@@ -288,9 +288,8 @@ def enable(bundles: tuple[str, ...], fail_mode: str, permissive: bool, session_n
288
288
  """
289
289
  if not bundles:
290
290
  # Fail loud, not a silent no-op: the CLI is the explicit surface and requires
291
- # the bundles to enable. Restore-from-intent (re-enable the session's configured
292
- # bundles) is the interactive `%policy enable` shortcut's job, not the CLI's --
293
- # see design_workflows.md "Re-enable enforcement".
291
+ # the bundles to enable. The interactive `%policy enable` surface also requires
292
+ # explicit bundles; its distinction is that it writes overrides rather than intent.
294
293
  print_error_with_tip(
295
294
  "No policy bundles specified.",
296
295
  "Use --bundle tdd and/or --bundle coding_standards.",
@@ -139,8 +139,10 @@ _SESSION_OPTION = click.option(
139
139
  def session_lane() -> None:
140
140
  """Bind a consumer's LLM-work to a (runtime, backend, model) lane for the session.
141
141
 
142
- Lanes are session-owned intent, frozen at first dispatch. The supervisor also
143
- accepts 'forge policy supervisor set --runtime/--backend' (same storage).
142
+ Lanes are session-owned intent. An explicit supervisor lane freezes at its first
143
+ registered policy check; auxiliary-consumer lanes freeze at first real dispatch.
144
+ The supervisor also accepts
145
+ 'forge policy supervisor set <target> --runtime/--backend' (same storage).
144
146
 
145
147
  \b
146
148
  Examples:
@@ -1,7 +1,8 @@
1
1
  """Shared LLM client abstraction for Forge components.
2
2
 
3
- This module provides a unified, async-first interface for calling LLMs
4
- across different providers (LiteLLM, Anthropic).
3
+ This module provides a unified, async-first interface for calling LLMs through
4
+ remote/local LiteLLM endpoints and OpenRouter directly. A native Anthropic
5
+ client is not implemented yet; ``anthropic/`` model ids route through LiteLLM.
5
6
 
6
7
  Usage:
7
8
  # Async usage (Proxy)
@@ -2,6 +2,7 @@
2
2
 
3
3
  Currently implemented:
4
4
  - LiteLLMClient: For both remote and local LiteLLM instances
5
+ - OpenRouterClient: Direct OpenRouter through its OpenAI-compatible endpoint
5
6
 
6
7
  Deferred (not yet implemented):
7
8
  - AnthropicClient: Direct Anthropic API
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import json
13
13
  import os
14
+ import stat
14
15
  import tempfile
15
16
  from pathlib import Path
16
17
  from typing import Any
@@ -60,6 +61,7 @@ def atomic_write_text(
60
61
  content: str,
61
62
  *,
62
63
  mode: int | None = None,
64
+ preserve_existing_mode: bool = False,
63
65
  create_parents: bool = True,
64
66
  ) -> None:
65
67
  """Write text to a file atomically.
@@ -72,12 +74,21 @@ def atomic_write_text(
72
74
  path: Target file path.
73
75
  content: Text content to write.
74
76
  mode: Optional final file mode (for example, 0o600).
77
+ preserve_existing_mode: Preserve the target's permission bits when it exists.
78
+ A missing target retains the secure tempfile default. Mutually exclusive with ``mode``.
75
79
  create_parents: Create parent directories if they don't exist.
76
80
 
77
81
  Raises:
82
+ ValueError: If ``mode`` and ``preserve_existing_mode`` are both requested.
78
83
  OSError: If the write or rename fails.
79
84
  """
80
- atomic_write_bytes(path, content.encode("utf-8"), mode=mode, create_parents=create_parents)
85
+ atomic_write_bytes(
86
+ path,
87
+ content.encode("utf-8"),
88
+ mode=mode,
89
+ preserve_existing_mode=preserve_existing_mode,
90
+ create_parents=create_parents,
91
+ )
81
92
 
82
93
 
83
94
  def atomic_write_bytes(
@@ -85,6 +96,7 @@ def atomic_write_bytes(
85
96
  content: bytes,
86
97
  *,
87
98
  mode: int | None = None,
99
+ preserve_existing_mode: bool = False,
88
100
  create_parents: bool = True,
89
101
  ) -> None:
90
102
  """Write bytes to a file atomically.
@@ -97,11 +109,24 @@ def atomic_write_bytes(
97
109
  path: Target file path.
98
110
  content: Bytes to write.
99
111
  mode: Optional final file mode (for example, 0o600).
112
+ preserve_existing_mode: Preserve the target's permission bits when it exists.
113
+ A missing target retains the secure tempfile default. Mutually exclusive with ``mode``.
100
114
  create_parents: Create parent directories if they don't exist.
101
115
 
102
116
  Raises:
117
+ ValueError: If ``mode`` and ``preserve_existing_mode`` are both requested.
103
118
  OSError: If the write or rename fails.
104
119
  """
120
+ if mode is not None and preserve_existing_mode:
121
+ raise ValueError("mode and preserve_existing_mode are mutually exclusive")
122
+
123
+ resolved_mode = mode
124
+ if preserve_existing_mode:
125
+ try:
126
+ resolved_mode = stat.S_IMODE(path.stat().st_mode)
127
+ except FileNotFoundError:
128
+ resolved_mode = None
129
+
105
130
  if create_parents:
106
131
  path.parent.mkdir(parents=True, exist_ok=True)
107
132
 
@@ -115,8 +140,8 @@ def atomic_write_bytes(
115
140
  with os.fdopen(fd, "wb") as f:
116
141
  f.write(content)
117
142
  f.flush()
118
- if mode is not None:
119
- os.fchmod(f.fileno(), mode)
143
+ if resolved_mode is not None:
144
+ os.fchmod(f.fileno(), resolved_mode)
120
145
  os.fsync(f.fileno())
121
146
  os.replace(temp_path, str(path))
122
147
  try:
@@ -440,7 +440,7 @@ def apply_codex_merge(config_path: Path, entries: tuple[CodexHookEntry, ...]) ->
440
440
 
441
441
  backup_path = backup_codex_config(config_path)
442
442
  config_path.parent.mkdir(parents=True, exist_ok=True)
443
- atomic_write_text(config_path, new_text)
443
+ atomic_write_text(config_path, new_text, preserve_existing_mode=True)
444
444
  return backup_path
445
445
 
446
446
 
@@ -548,7 +548,7 @@ def remove_codex_block(config_path: Path, entries: tuple[CodexHookEntry, ...]) -
548
548
  config_path.unlink()
549
549
  return CodexRemoveResult(removed=True, deleted_file=True, leftover_commands=leftover)
550
550
 
551
- atomic_write_text(config_path, new_text)
551
+ atomic_write_text(config_path, new_text, preserve_existing_mode=True)
552
552
  return CodexRemoveResult(removed=True, leftover_commands=leftover)
553
553
 
554
554
 
@@ -109,15 +109,14 @@ _CLAUDE_MODEL_PIN_ENV_VARS = (
109
109
  "ANTHROPIC_DEFAULT_OPUS_MODEL",
110
110
  )
111
111
 
112
- # The supervisor as a consumer-lane binding (epic consumer_lanes, ticket T3). It
113
- # reads repo files (a `tool_agent` capability floor) and today always runs as
114
- # `claude -p`. In T3 only `runtime_id` is load-bearing -- it selects the dispatch
115
- # arm in `_dispatch_supervisor`. `backend_id`/`model` are nominal placement
116
- # metadata: the `claude_code` arm still derives transport (`base_url`) and the
117
- # `opus` pin dynamically (byte-identical contract), so they are not yet consulted.
118
- # `anthropic-direct` is a real catalog source (no single backend is "correct" for a
119
- # proxied supervisor, which routes through whatever proxy). T2 makes backend
120
- # load-bearing; T4 adds the `codex` arm + a narrow `SupervisorConfig` override.
112
+ # The supervisor as a consumer-lane binding (epic consumer_lanes). It reads repo
113
+ # files (a `tool_agent` capability floor), and `runtime_id` selects the dispatch arm
114
+ # in `_dispatch_supervisor`: `claude_code` or `codex`. `backend_id`/`model` remain
115
+ # nominal placement metadata for dispatch: the Claude arm derives transport
116
+ # (`base_url`) and its `opus` pin dynamically, while Codex chooses its own model.
117
+ # `backend_id` is nevertheless load-bearing for billing (for example `claude-max`).
118
+ # `anthropic-direct` is a real catalog source; no single backend is "correct" for a
119
+ # proxied Claude supervisor, which routes through whichever proxy was resolved.
121
120
  SUPERVISOR_CONSUMER = Consumer(
122
121
  id="supervisor",
123
122
  capability_floor="tool_agent",
@@ -494,9 +493,9 @@ def _dispatch_supervisor(
494
493
  ) -> SessionResult:
495
494
  """Dispatch one supervisor run on the resolved lane's runtime.
496
495
 
497
- T3 implements only the ``claude_code`` arm (the existing path, verbatim).
498
- The ``codex`` arm lands in T4 (epic consumer_lanes); any other runtime has
499
- no supervisor adapter.
496
+ The shipped arms are ``claude_code`` (native resume, optionally proxied) and
497
+ ``codex`` (fresh, plan-fed ``codex exec``). Any other runtime has no supervisor
498
+ adapter and fails open through ``_SupervisorRoutingError``.
500
499
  """
501
500
  runtime = lane.runtime_id
502
501
  if runtime == "claude_code":
@@ -81,7 +81,7 @@ def confirmed_lane(state: SessionState, consumer: Consumer) -> LaneRecord | None
81
81
  """Return ``consumer``'s frozen lane record, or None if not yet bound.
82
82
 
83
83
  The resolving commands' already-bound reject reads this: a non-None result
84
- means the lane is frozen, so ``policy supervisor set --runtime`` must refuse to
84
+ means the lane is frozen, so ``policy supervisor set <target> --runtime`` must refuse to
85
85
  change it (the binding is immutable once written, card D2).
86
86
  """
87
87
  binding = _confirmed_binding(state, consumer)
@@ -148,9 +148,10 @@ def set_intent_lane(state: SessionState, consumer: Consumer, lane_record: LaneRe
148
148
  """Record ``consumer``'s requested lane in ``intent`` (the resolving-command setter).
149
149
 
150
150
  The write companion to ``read_bound_lane``'s intent branch: resolving commands
151
- (start/fork ``--supervisor-runtime``, ``policy supervisor set --runtime``) call this
152
- to record the requested placement. The freeze (``ensure_consumer_lane_binding``) later
153
- copies the lane that actually dispatched into ``confirmed``. Callers enforce the
151
+ (start/fork ``--supervisor-runtime``, ``policy supervisor set <target> --runtime``) call this
152
+ to record the requested placement. ``ensure_consumer_lane_binding`` later freezes the
153
+ lane at the consumer's lifecycle commitment point: first registered policy check for
154
+ the supervisor, first real dispatch for auxiliary consumers. Callers enforce the
154
155
  already-bound reject (``confirmed_lane``) before invoking this -- it does not itself
155
156
  guard against overwriting a request on an already-frozen session.
156
157
  """
@@ -192,10 +193,12 @@ def clear_intent_lane(state: SessionState, consumer: Consumer) -> None:
192
193
  def ensure_consumer_lane_binding(state: SessionState, consumer: Consumer, lane_record: LaneRecord | None) -> None:
193
194
  """Freeze ``consumer``'s explicitly-chosen lane into ``confirmed``, write-if-absent.
194
195
 
195
- The single immutability seam (card D2). ``lane_record`` is the lane the hook injected at
196
- registration (``read_bound_lane`` -- confirmed-first, else the intent override), NOT a fresh
197
- manifest read, so the freeze records exactly what dispatched even if intent changes during
198
- the (multi-second) supervisor call.
196
+ The single immutability seam (card D2). ``lane_record`` is the lane selected for the
197
+ consumer's lifecycle commitment (``read_bound_lane`` -- confirmed-first, else the intent
198
+ override), NOT a fresh manifest read. For the supervisor that commitment is the registered
199
+ policy check and may precede a runtime dispatch; auxiliary callers invoke this only after
200
+ committing to a real dispatch. The threaded value prevents a concurrent intent change from
201
+ freezing a lane the lifecycle event did not select.
199
202
 
200
203
  **The default lane is never frozen.** ``lane_record is None`` means no explicit choice was
201
204
  recorded (neither ``confirmed`` nor ``intent``) and the consumer ran on its default; we leave
@@ -209,7 +209,7 @@ def validate_key(key: str) -> list[str]:
209
209
 
210
210
  if first_part == "consumer_lanes":
211
211
  # Consumer-lane bindings (epic consumer_lanes, T1b) are set only through resolving commands
212
- # (--supervisor-runtime at start/fork, 'forge policy supervisor set --runtime'), which expand a
212
+ # (--supervisor-runtime at start/fork, 'forge policy supervisor set <target> --runtime'), which expand a
213
213
  # runtime to a full validated LaneRecord and enforce the already-bound reject. A raw override --
214
214
  # partial (can't rehydrate a 3-field LaneRecord) or full-object (bypasses that validation, and
215
215
  # after first dispatch is recorded-but-ignored since dispatch reads the frozen confirmed binding)
@@ -217,7 +217,8 @@ def validate_key(key: str) -> list[str]:
217
217
  raise InvalidOverrideKeyError(
218
218
  key,
219
219
  "consumer lanes are set via resolving commands, not overrides",
220
- hint="use --supervisor-runtime at session start/fork or 'forge policy supervisor set --runtime'",
220
+ hint="use --supervisor-runtime at session start/fork or "
221
+ "'forge policy supervisor set <target> --runtime'",
221
222
  )
222
223
 
223
224
  if "*" in key: