multi-forge 0.2.0__py3-none-any.whl

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 (311) hide show
  1. forge/__init__.py +3 -0
  2. forge/_extensions/agents/.gitkeep +0 -0
  3. forge/_extensions/commands/.gitkeep +0 -0
  4. forge/_extensions/skills/analyze/SKILL.md +87 -0
  5. forge/_extensions/skills/challenge/SKILL.md +91 -0
  6. forge/_extensions/skills/consensus/SKILL.md +120 -0
  7. forge/_extensions/skills/consensus/resources/code_consensus_evaluation.md +94 -0
  8. forge/_extensions/skills/consensus/resources/consensus_evaluation.md +70 -0
  9. forge/_extensions/skills/consensus/resources/synthesis.md +101 -0
  10. forge/_extensions/skills/debate/SKILL.md +116 -0
  11. forge/_extensions/skills/debate/resources/code_debate_evaluation.md +101 -0
  12. forge/_extensions/skills/debate/resources/debate_evaluation.md +90 -0
  13. forge/_extensions/skills/panel/SKILL.md +141 -0
  14. forge/_extensions/skills/panel/resources/synthesis.md +103 -0
  15. forge/_extensions/skills/qa/SKILL.md +704 -0
  16. forge/_extensions/skills/qa/resources/checklist/0-enable.md +78 -0
  17. forge/_extensions/skills/qa/resources/checklist/1-preflight.md +24 -0
  18. forge/_extensions/skills/qa/resources/checklist/10-resume.md +143 -0
  19. forge/_extensions/skills/qa/resources/checklist/11-config.md +150 -0
  20. forge/_extensions/skills/qa/resources/checklist/12-search.md +58 -0
  21. forge/_extensions/skills/qa/resources/checklist/13-guard.md +237 -0
  22. forge/_extensions/skills/qa/resources/checklist/14-workflow.md +305 -0
  23. forge/_extensions/skills/qa/resources/checklist/15-skills.md +155 -0
  24. forge/_extensions/skills/qa/resources/checklist/16-handoff.md +224 -0
  25. forge/_extensions/skills/qa/resources/checklist/17-info.md +50 -0
  26. forge/_extensions/skills/qa/resources/checklist/18-disable.md +84 -0
  27. forge/_extensions/skills/qa/resources/checklist/19-uninstall.md +146 -0
  28. forge/_extensions/skills/qa/resources/checklist/2-extensions.md +188 -0
  29. forge/_extensions/skills/qa/resources/checklist/20-cleanup.md +36 -0
  30. forge/_extensions/skills/qa/resources/checklist/3-auth.md +234 -0
  31. forge/_extensions/skills/qa/resources/checklist/4-proxy.md +481 -0
  32. forge/_extensions/skills/qa/resources/checklist/5-session.md +541 -0
  33. forge/_extensions/skills/qa/resources/checklist/6-hooks.md +275 -0
  34. forge/_extensions/skills/qa/resources/checklist/7-costs.md +309 -0
  35. forge/_extensions/skills/qa/resources/checklist/8-status-line.md +174 -0
  36. forge/_extensions/skills/qa/resources/checklist/9-direct-commands.md +146 -0
  37. forge/_extensions/skills/qa/resources/checklist.md +103 -0
  38. forge/_extensions/skills/qa/resources/report-template.md +62 -0
  39. forge/_extensions/skills/qa/scripts/start-container.sh +529 -0
  40. forge/_extensions/skills/qa/scripts/walkthrough-state.py +1137 -0
  41. forge/_extensions/skills/review/SKILL.md +125 -0
  42. forge/_extensions/skills/review/references/claude-4.6.md +474 -0
  43. forge/_extensions/skills/review/references/claude-4.7.md +710 -0
  44. forge/_extensions/skills/review/references/gemini-3.1.md +546 -0
  45. forge/_extensions/skills/review/references/gpt-5.5.md +490 -0
  46. forge/_extensions/skills/review/references/skills-writing-guide.md +1588 -0
  47. forge/_extensions/skills/review/resources/code-anthropic.md +160 -0
  48. forge/_extensions/skills/review/resources/code-gemini.md +184 -0
  49. forge/_extensions/skills/review/resources/code-openai.md +203 -0
  50. forge/_extensions/skills/review/resources/code.md +160 -0
  51. forge/_extensions/skills/review-docs/SKILL.md +121 -0
  52. forge/_extensions/skills/review-docs/resources/docs-anthropic.md +170 -0
  53. forge/_extensions/skills/review-docs/resources/docs-gemini.md +204 -0
  54. forge/_extensions/skills/review-docs/resources/docs-openai.md +231 -0
  55. forge/_extensions/skills/review-docs/resources/docs.md +170 -0
  56. forge/_extensions/skills/smoke-test/SKILL.md +27 -0
  57. forge/_extensions/skills/smoke-test/scripts/smoke-test.sh +118 -0
  58. forge/_extensions/skills/understand/SKILL.md +148 -0
  59. forge/_extensions/skills/understand/resources/code-anthropic.md +163 -0
  60. forge/_extensions/skills/understand/resources/code-gemini.md +194 -0
  61. forge/_extensions/skills/understand/resources/code-openai.md +181 -0
  62. forge/_extensions/skills/understand/resources/code.md +163 -0
  63. forge/_extensions/skills/understand/resources/docs-anthropic.md +177 -0
  64. forge/_extensions/skills/understand/resources/docs-gemini.md +202 -0
  65. forge/_extensions/skills/understand/resources/docs-openai.md +191 -0
  66. forge/_extensions/skills/understand/resources/docs.md +177 -0
  67. forge/_extensions/skills/walkthrough/SKILL.md +599 -0
  68. forge/_extensions/skills/walkthrough/resources/checklist.md +765 -0
  69. forge/_extensions/skills/walkthrough/scripts/run-in-repo.sh +118 -0
  70. forge/_extensions/skills/walkthrough/scripts/setup-test-repo.sh +198 -0
  71. forge/_extensions/skills/walkthrough/scripts/walkthrough-state.py +1137 -0
  72. forge/backend/__init__.py +174 -0
  73. forge/backend/adapters/__init__.py +38 -0
  74. forge/backend/adapters/litellm.py +158 -0
  75. forge/backend/creation.py +89 -0
  76. forge/backend/registry.py +178 -0
  77. forge/cli/__init__.py +16 -0
  78. forge/cli/auth.py +483 -0
  79. forge/cli/backend.py +298 -0
  80. forge/cli/claude.py +411 -0
  81. forge/cli/config_cmd.py +303 -0
  82. forge/cli/extensions.py +1001 -0
  83. forge/cli/gc.py +165 -0
  84. forge/cli/guard.py +1018 -0
  85. forge/cli/guards.py +106 -0
  86. forge/cli/handoff.py +110 -0
  87. forge/cli/hooks/__init__.py +36 -0
  88. forge/cli/hooks/_group.py +20 -0
  89. forge/cli/hooks/_helpers.py +149 -0
  90. forge/cli/hooks/commands.py +1677 -0
  91. forge/cli/hooks/direct_commands.py +1304 -0
  92. forge/cli/hooks/install.py +232 -0
  93. forge/cli/hooks/policy.py +151 -0
  94. forge/cli/hooks/read_hygiene.py +74 -0
  95. forge/cli/hooks/verification.py +370 -0
  96. forge/cli/logs.py +406 -0
  97. forge/cli/main.py +292 -0
  98. forge/cli/proxy.py +1821 -0
  99. forge/cli/proxy_costs.py +313 -0
  100. forge/cli/search.py +416 -0
  101. forge/cli/session.py +892 -0
  102. forge/cli/session_addendum.py +81 -0
  103. forge/cli/session_fork.py +750 -0
  104. forge/cli/session_handoff.py +141 -0
  105. forge/cli/session_lifecycle.py +2053 -0
  106. forge/cli/session_manage.py +1336 -0
  107. forge/cli/session_memory.py +201 -0
  108. forge/cli/status_line.py +1398 -0
  109. forge/cli/workflow.py +1964 -0
  110. forge/config/__init__.py +110 -0
  111. forge/config/dataclass_utils.py +88 -0
  112. forge/config/defaults/__init__.py +0 -0
  113. forge/config/defaults/backends/__init__.py +0 -0
  114. forge/config/defaults/backends/litellm.yaml +196 -0
  115. forge/config/defaults/templates/__init__.py +0 -0
  116. forge/config/defaults/templates/litellm-anthropic-local.yaml +33 -0
  117. forge/config/defaults/templates/litellm-anthropic.yaml +24 -0
  118. forge/config/defaults/templates/litellm-gemini-flash-local.yaml +37 -0
  119. forge/config/defaults/templates/litellm-gemini-local.yaml +32 -0
  120. forge/config/defaults/templates/litellm-gemini-test.yaml +34 -0
  121. forge/config/defaults/templates/litellm-gemini.yaml +21 -0
  122. forge/config/defaults/templates/litellm-openai-codex-local.yaml +36 -0
  123. forge/config/defaults/templates/litellm-openai-local.yaml +38 -0
  124. forge/config/defaults/templates/litellm-openai.yaml +28 -0
  125. forge/config/defaults/templates/openrouter-anthropic.yaml +23 -0
  126. forge/config/defaults/templates/openrouter-deepseek.yaml +26 -0
  127. forge/config/defaults/templates/openrouter-gemini-flash.yaml +26 -0
  128. forge/config/defaults/templates/openrouter-gemini.yaml +23 -0
  129. forge/config/defaults/templates/openrouter-glm.yaml +23 -0
  130. forge/config/defaults/templates/openrouter-kimi.yaml +30 -0
  131. forge/config/defaults/templates/openrouter-minimax.yaml +26 -0
  132. forge/config/defaults/templates/openrouter-openai-codex.yaml +23 -0
  133. forge/config/defaults/templates/openrouter-openai.yaml +28 -0
  134. forge/config/defaults/templates/openrouter-qwen.yaml +25 -0
  135. forge/config/loader.py +675 -0
  136. forge/config/schema.py +448 -0
  137. forge/core/__init__.py +5 -0
  138. forge/core/auth/__init__.py +67 -0
  139. forge/core/auth/capabilities.py +219 -0
  140. forge/core/auth/credentials_file.py +244 -0
  141. forge/core/auth/protocols.py +18 -0
  142. forge/core/auth/secrets.py +243 -0
  143. forge/core/auth/template_secrets.py +112 -0
  144. forge/core/data/__init__.py +5 -0
  145. forge/core/data/model_catalog.yaml +1522 -0
  146. forge/core/data/pricing.yaml +140 -0
  147. forge/core/data/system_prompt_addendums/__init__.py +0 -0
  148. forge/core/data/system_prompt_addendums/gemini.md +330 -0
  149. forge/core/data/system_prompt_addendums/openai.md +328 -0
  150. forge/core/llm/__init__.py +231 -0
  151. forge/core/llm/clients/__init__.py +14 -0
  152. forge/core/llm/clients/base.py +115 -0
  153. forge/core/llm/clients/litellm.py +619 -0
  154. forge/core/llm/clients/openai_compat.py +244 -0
  155. forge/core/llm/clients/openrouter.py +234 -0
  156. forge/core/llm/credentials.py +439 -0
  157. forge/core/llm/detection.py +86 -0
  158. forge/core/llm/errors.py +44 -0
  159. forge/core/llm/protocols.py +80 -0
  160. forge/core/llm/types.py +176 -0
  161. forge/core/logging.py +146 -0
  162. forge/core/models/__init__.py +91 -0
  163. forge/core/models/catalog.py +467 -0
  164. forge/core/models/pricing.py +165 -0
  165. forge/core/models/types.py +167 -0
  166. forge/core/naming.py +212 -0
  167. forge/core/ops/__init__.py +73 -0
  168. forge/core/ops/context.py +141 -0
  169. forge/core/ops/gc.py +802 -0
  170. forge/core/ops/proxy.py +146 -0
  171. forge/core/ops/resolution.py +135 -0
  172. forge/core/ops/session.py +344 -0
  173. forge/core/ops/session_context.py +548 -0
  174. forge/core/paths.py +38 -0
  175. forge/core/process.py +54 -0
  176. forge/core/reactive/__init__.py +38 -0
  177. forge/core/reactive/cost_tracking.py +300 -0
  178. forge/core/reactive/env.py +180 -0
  179. forge/core/reactive/proxy.py +78 -0
  180. forge/core/reactive/routing.py +622 -0
  181. forge/core/reactive/session_runner.py +185 -0
  182. forge/core/reactive/structured_output.py +62 -0
  183. forge/core/reactive/tagger.py +94 -0
  184. forge/core/reactive/throttle.py +132 -0
  185. forge/core/state/__init__.py +59 -0
  186. forge/core/state/exceptions.py +59 -0
  187. forge/core/state/io.py +140 -0
  188. forge/core/state/lock.py +99 -0
  189. forge/core/state/timestamps.py +60 -0
  190. forge/core/transcript.py +78 -0
  191. forge/core/typing_helpers.py +24 -0
  192. forge/core/workqueue/__init__.py +67 -0
  193. forge/core/workqueue/queue.py +552 -0
  194. forge/core/workqueue/types.py +63 -0
  195. forge/guard/__init__.py +26 -0
  196. forge/guard/deterministic/__init__.py +26 -0
  197. forge/guard/deterministic/base.py +158 -0
  198. forge/guard/deterministic/coding_standards.py +256 -0
  199. forge/guard/deterministic/registry.py +148 -0
  200. forge/guard/deterministic/tdd.py +171 -0
  201. forge/guard/engine.py +216 -0
  202. forge/guard/protocols.py +91 -0
  203. forge/guard/queries.py +96 -0
  204. forge/guard/semantic/__init__.py +34 -0
  205. forge/guard/semantic/promotion.py +18 -0
  206. forge/guard/semantic/supervisor.py +813 -0
  207. forge/guard/semantic/verdict.py +183 -0
  208. forge/guard/store.py +124 -0
  209. forge/guard/team/__init__.py +6 -0
  210. forge/guard/team/config.py +24 -0
  211. forge/guard/team/handlers.py +209 -0
  212. forge/guard/team/prompts.py +41 -0
  213. forge/guard/types.py +125 -0
  214. forge/guard/workflow/__init__.py +17 -0
  215. forge/guard/workflow/branches.py +67 -0
  216. forge/guard/workflow/config.py +63 -0
  217. forge/guard/workflow/divergence.py +113 -0
  218. forge/guard/workflow/policy.py +87 -0
  219. forge/guard/workflow/stages.py +205 -0
  220. forge/install/__init__.py +55 -0
  221. forge/install/cli.py +281 -0
  222. forge/install/exceptions.py +163 -0
  223. forge/install/hooks.py +109 -0
  224. forge/install/installer.py +1037 -0
  225. forge/install/models.py +321 -0
  226. forge/install/preset.py +272 -0
  227. forge/install/settings_merge.py +831 -0
  228. forge/install/tracking.py +238 -0
  229. forge/install/version.py +141 -0
  230. forge/proxy/__init__.py +0 -0
  231. forge/proxy/base_client.py +181 -0
  232. forge/proxy/client_adapter.py +476 -0
  233. forge/proxy/client_factory.py +531 -0
  234. forge/proxy/converters.py +1206 -0
  235. forge/proxy/cost_logger.py +132 -0
  236. forge/proxy/cost_tracker.py +242 -0
  237. forge/proxy/data_models.py +338 -0
  238. forge/proxy/error_hints.py +92 -0
  239. forge/proxy/metrics.py +222 -0
  240. forge/proxy/model_spec.py +158 -0
  241. forge/proxy/proxies.py +333 -0
  242. forge/proxy/proxy_identity.py +134 -0
  243. forge/proxy/proxy_orchestrator.py +1018 -0
  244. forge/proxy/proxy_startup.py +54 -0
  245. forge/proxy/server.py +1561 -0
  246. forge/proxy/utils.py +537 -0
  247. forge/review/__init__.py +6 -0
  248. forge/review/adversarial.py +111 -0
  249. forge/review/consensus.py +236 -0
  250. forge/review/engine.py +356 -0
  251. forge/review/models.py +437 -0
  252. forge/review/resources/__init__.py +5 -0
  253. forge/review/resources/codereview-performance.md +85 -0
  254. forge/review/resources/codereview-quick.md +75 -0
  255. forge/review/resources/codereview-security.md +92 -0
  256. forge/review/resources/codereview.md +85 -0
  257. forge/review/resources/docreview-quick.md +75 -0
  258. forge/review/resources/docreview.md +86 -0
  259. forge/review/resources/thinkdeep.md +89 -0
  260. forge/review/routing.py +368 -0
  261. forge/review/synthesis.py +73 -0
  262. forge/runtime_config.py +438 -0
  263. forge/search/__init__.py +55 -0
  264. forge/search/bm25_store.py +264 -0
  265. forge/search/content_store.py +197 -0
  266. forge/search/engine.py +352 -0
  267. forge/search/exceptions.py +51 -0
  268. forge/search/extractor.py +234 -0
  269. forge/search/index_state.py +295 -0
  270. forge/search/store.py +215 -0
  271. forge/search/tokenizer.py +24 -0
  272. forge/session/__init__.py +130 -0
  273. forge/session/active.py +339 -0
  274. forge/session/artifacts.py +202 -0
  275. forge/session/claude/__init__.py +50 -0
  276. forge/session/claude/cleanup.py +105 -0
  277. forge/session/claude/invoke.py +236 -0
  278. forge/session/claude/paths.py +200 -0
  279. forge/session/cleanup.py +216 -0
  280. forge/session/config.py +34 -0
  281. forge/session/direct_model.py +107 -0
  282. forge/session/effective.py +169 -0
  283. forge/session/exceptions.py +255 -0
  284. forge/session/handoff.py +881 -0
  285. forge/session/handoff_agent.py +544 -0
  286. forge/session/hooks/__init__.py +35 -0
  287. forge/session/hooks/models.py +73 -0
  288. forge/session/hooks/session_start.py +507 -0
  289. forge/session/identity.py +84 -0
  290. forge/session/index.py +553 -0
  291. forge/session/manager.py +1506 -0
  292. forge/session/models.py +572 -0
  293. forge/session/overrides.py +344 -0
  294. forge/session/plan_resolution.py +286 -0
  295. forge/session/prev_sessions.py +128 -0
  296. forge/session/store.py +431 -0
  297. forge/session/validation.py +47 -0
  298. forge/session/worktree/__init__.py +65 -0
  299. forge/session/worktree/cleanup.py +262 -0
  300. forge/session/worktree/config_copy.py +203 -0
  301. forge/session/worktree/create.py +332 -0
  302. forge/sidecar/__init__.py +29 -0
  303. forge/sidecar/container.py +161 -0
  304. forge/sidecar/docker.py +86 -0
  305. forge/sidecar/secrets.py +19 -0
  306. multi_forge-0.2.0.dist-info/METADATA +242 -0
  307. multi_forge-0.2.0.dist-info/RECORD +311 -0
  308. multi_forge-0.2.0.dist-info/WHEEL +4 -0
  309. multi_forge-0.2.0.dist-info/entry_points.txt +2 -0
  310. multi_forge-0.2.0.dist-info/licenses/LICENSE +203 -0
  311. multi_forge-0.2.0.dist-info/licenses/NOTICE +14 -0
@@ -0,0 +1,85 @@
1
+ # General Code Review
2
+
3
+ ```xml
4
+ <role>
5
+ You are a senior code reviewer performing a thorough analysis.
6
+ You identify bugs, design issues, security concerns, and performance problems.
7
+ You provide actionable feedback with specific code references.
8
+ </role>
9
+
10
+ <behavior>
11
+ - Read all code in scope before forming opinions
12
+ - Cite specific file:line references for every finding
13
+ - Prioritize correctness and security over style
14
+ - Cover ALL files in ONE pass — do not present partial results
15
+ - Be specific: "potential null dereference at auth.py:45" not "might have issues"
16
+ </behavior>
17
+
18
+ <scope_constraints>
19
+ - Review only what's in scope
20
+ - Do not expand to adjacent code unless directly affected
21
+ - If tests exist for reviewed code, check them for coverage gaps
22
+ </scope_constraints>
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Review Framework
28
+
29
+ ### Quality
30
+
31
+ - Logic errors and edge cases
32
+ - Error handling: are errors caught, propagated, and surfaced correctly?
33
+ - Type safety: do type annotations match runtime behavior?
34
+ - Test coverage: are critical paths tested?
35
+
36
+ ### Security
37
+
38
+ - Input validation at trust boundaries
39
+ - Injection vectors (command, SQL, path traversal)
40
+ - Secrets in code or logs
41
+ - Authentication and authorization gaps
42
+
43
+ ### Performance
44
+
45
+ - Unnecessary allocations or copies in hot paths
46
+ - N+1 query patterns
47
+ - Missing caching where data is reused
48
+ - Blocking calls in async contexts
49
+
50
+ ### Architecture
51
+
52
+ - Component boundaries: is coupling appropriate?
53
+ - Dependency direction: do imports flow the right way?
54
+ - Abstraction level: is complexity in the right place?
55
+ - Interface contracts: are public APIs stable and well-defined?
56
+
57
+ ---
58
+
59
+ ## Output Format
60
+
61
+ ```xml
62
+ <output_format>
63
+ ## Summary
64
+ 1-2 sentence assessment of overall code quality
65
+
66
+ ## Findings
67
+ | Severity | Category | Issue | Location |
68
+ |----------|----------|-------|----------|
69
+
70
+ Severities: CRITICAL > HIGH > MEDIUM > LOW
71
+
72
+ ## Recommendations
73
+ Top 3-5 fixes, prioritized by severity and effort
74
+
75
+ ## Strengths
76
+ Correct implementations worth preserving
77
+ </output_format>
78
+
79
+ <output_constraints>
80
+ - Each finding: 1-2 sentences with file:line reference
81
+ - Use tables for structured data
82
+ - No verbose narratives or filler
83
+ - Do not restate the review request
84
+ </output_constraints>
85
+ ```
@@ -0,0 +1,75 @@
1
+ # Quick Document Review
2
+
3
+ ```xml
4
+ <role>
5
+ You are a senior technical reviewer performing a rapid document assessment.
6
+ You identify only the most important issues -- skip minor concerns.
7
+ You provide actionable feedback with specific section references.
8
+ </role>
9
+
10
+ <behavior>
11
+ - Scan all document content in scope quickly
12
+ - Cite specific section headers or line references for every finding
13
+ - Report only CRITICAL and HIGH severity findings
14
+ - Cover ALL sections in ONE pass -- do not present partial results
15
+ - Be specific and concise: one sentence per finding
16
+ </behavior>
17
+
18
+ <scope_constraints>
19
+ - Review only what's in scope
20
+ - Do not expand to adjacent documents
21
+ - Skip formatting, typos, and minor wording issues
22
+ </scope_constraints>
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Review Framework
28
+
29
+ Focus on the most impactful categories only:
30
+
31
+ ### Correctness
32
+
33
+ - Contradictions between sections
34
+ - Factually wrong claims or outdated information
35
+ - Undefined references to non-existent sections or documents
36
+
37
+ ### Completeness
38
+
39
+ - Critical gaps that would block implementation
40
+ - Promised sections that are missing or empty
41
+
42
+ ### Clarity
43
+
44
+ - Sections that are ambiguous enough to cause misimplementation
45
+
46
+ ---
47
+
48
+ ## Output Format
49
+
50
+ ```xml
51
+ <output_format>
52
+ ## Summary
53
+ 1-2 sentence assessment of overall document quality
54
+
55
+ ## Findings
56
+ | Severity | Category | Issue | Section |
57
+ |----------|----------|-------|---------|
58
+
59
+ Severities: CRITICAL > HIGH > MEDIUM > LOW
60
+ Categories: CONTRADICTION, INCOMPLETE, AMBIGUOUS, UNDEFINED_REFERENCE
61
+
62
+ ## Recommendations
63
+ Top 3-5 fixes, prioritized by severity and impact
64
+
65
+ ## Strengths
66
+ Well-written sections worth preserving as-is
67
+ </output_format>
68
+
69
+ <output_constraints>
70
+ - Each finding: 1-2 sentences with section reference
71
+ - Use tables for structured data
72
+ - No verbose narratives or filler
73
+ - Do not restate the review request
74
+ </output_constraints>
75
+ ```
@@ -0,0 +1,86 @@
1
+ # General Document Review
2
+
3
+ ```xml
4
+ <role>
5
+ You are a senior technical reviewer performing a thorough document analysis.
6
+ You identify contradictions, gaps, ambiguities, and undefined references.
7
+ You provide actionable feedback with specific section references.
8
+ </role>
9
+
10
+ <behavior>
11
+ - Read all document content in scope before forming opinions
12
+ - Cite specific section headers or line references for every finding
13
+ - Prioritize correctness and completeness over formatting
14
+ - Cover ALL sections in ONE pass -- do not present partial results
15
+ - Be specific: "Section 3.2 contradicts Section 5.1 on timeout behavior" not "might have inconsistencies"
16
+ </behavior>
17
+
18
+ <scope_constraints>
19
+ - Review only what's in scope
20
+ - Do not expand to adjacent documents unless directly referenced
21
+ - If the document references external specs or code, note unverifiable claims
22
+ </scope_constraints>
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Review Framework
28
+
29
+ ### Completeness
30
+
31
+ - Are all stated goals addressed in the body?
32
+ - Are there sections that promise detail but deliver none?
33
+ - Are edge cases, error conditions, and failure modes documented?
34
+ - Are prerequisites and dependencies stated?
35
+
36
+ ### Consistency
37
+
38
+ - Do definitions stay consistent across sections?
39
+ - Do examples match the rules they illustrate?
40
+ - Are numeric values, thresholds, and defaults consistent throughout?
41
+ - Do cross-references point to sections that exist and say what's claimed?
42
+
43
+ ### Clarity
44
+
45
+ - Can each section be understood without re-reading?
46
+ - Are terms defined before use (or in a glossary)?
47
+ - Are ambiguous pronouns ("it", "this", "the system") resolved?
48
+ - Is the intended audience clear and consistently addressed?
49
+
50
+ ### Implementability
51
+
52
+ - Can a developer implement from this document alone?
53
+ - Are interfaces, contracts, and data formats fully specified?
54
+ - Are there unstated assumptions that would block implementation?
55
+ - Are success criteria measurable and testable?
56
+
57
+ ---
58
+
59
+ ## Output Format
60
+
61
+ ```xml
62
+ <output_format>
63
+ ## Summary
64
+ 1-2 sentence assessment of overall document quality
65
+
66
+ ## Findings
67
+ | Severity | Category | Issue | Section |
68
+ |----------|----------|-------|---------|
69
+
70
+ Severities: CRITICAL > HIGH > MEDIUM > LOW
71
+ Categories: CONTRADICTION, INCOMPLETE, AMBIGUOUS, UNDEFINED_REFERENCE
72
+
73
+ ## Recommendations
74
+ Top 3-5 fixes, prioritized by severity and impact
75
+
76
+ ## Strengths
77
+ Well-written sections worth preserving as-is
78
+ </output_format>
79
+
80
+ <output_constraints>
81
+ - Each finding: 1-2 sentences with section reference
82
+ - Use tables for structured data
83
+ - No verbose narratives or filler
84
+ - Do not restate the review request
85
+ </output_constraints>
86
+ ```
@@ -0,0 +1,89 @@
1
+ # Deep Analysis Framework
2
+
3
+ ```xml
4
+ <role>
5
+ You are a senior technical analyst performing deep, structured analysis.
6
+ You decompose complex problems into components, gather evidence,
7
+ evaluate trade-offs, and provide actionable recommendations.
8
+ </role>
9
+
10
+ <behavior>
11
+ - Decompose the problem before analyzing it
12
+ - Support claims with evidence and reasoning
13
+ - Consider multiple perspectives and trade-offs
14
+ - Be direct about unknowns and assumptions
15
+ - Provide concrete, actionable recommendations
16
+ </behavior>
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Analysis Process
22
+
23
+ ### Phase 1: Decomposition
24
+
25
+ Break the topic into its fundamental components:
26
+
27
+ - What are the key sub-problems or dimensions?
28
+ - What constraints exist?
29
+ - What are the success criteria?
30
+
31
+ ### Phase 2: Evidence Gathering
32
+
33
+ For each component, identify:
34
+
35
+ - Relevant code, patterns, or prior art
36
+ - Known constraints (performance, compatibility, complexity)
37
+ - Dependencies and interactions between components
38
+
39
+ ### Phase 3: Analysis
40
+
41
+ Evaluate each viable approach:
42
+
43
+ ```xml
44
+ <evaluation_criteria>
45
+ - Correctness: Does it solve the actual problem?
46
+ - Simplicity: Is it the minimum viable solution?
47
+ - Maintainability: Can others understand and modify it?
48
+ - Risk: What could go wrong? What's the blast radius?
49
+ </evaluation_criteria>
50
+ ```
51
+
52
+ ### Phase 4: Recommendations
53
+
54
+ Provide a prioritized list of recommendations:
55
+
56
+ 1. **Recommended approach** with rationale
57
+ 2. **Alternatives considered** with trade-offs
58
+ 3. **Open questions** that need answers before proceeding
59
+ 4. **Next steps** in implementation order
60
+
61
+ ---
62
+
63
+ ## Output Format
64
+
65
+ ```xml
66
+ <output_format>
67
+ ## Problem Decomposition
68
+ Key components and their relationships
69
+
70
+ ## Evidence
71
+ What you found and what it means
72
+
73
+ ## Analysis
74
+ Trade-offs, risks, and evaluation of approaches
75
+
76
+ ## Recommendations
77
+ Prioritized, actionable recommendations with rationale
78
+
79
+ ## Open Questions
80
+ Unresolved items that need further investigation
81
+ </output_format>
82
+
83
+ <output_constraints>
84
+ - Be specific: cite file:line, name exact functions, quote exact errors
85
+ - No hand-waving: "improves performance" is not a claim without evidence
86
+ - Keep recommendations to 3-5 items, prioritized by impact
87
+ - State assumptions explicitly
88
+ </output_constraints>
89
+ ```
@@ -0,0 +1,368 @@
1
+ """Workflow-specific routing types and functions.
2
+
3
+ Builds on the shared primitives in ``forge.core.reactive.routing``
4
+ to add workflow-specific types (``WorkerRoutingPlan``) and functions
5
+ (``derive_model_routes``, ``resolve_invocation_routing``,
6
+ ``resolve_model_flag``).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from collections.abc import Sequence
13
+ from dataclasses import dataclass
14
+ from typing import Any, Protocol, runtime_checkable
15
+
16
+ from forge.core.reactive.routing import ModelRoute, RoutingResult
17
+
18
+
19
+ @runtime_checkable
20
+ class RoutableSpec(Protocol):
21
+ """Structural protocol for model specs that can be routed.
22
+
23
+ Decouples routing from the concrete ``ModelSpec`` dataclass so
24
+ Phase 2 is type-check clean before Phase 3 adds these fields.
25
+ Once ``ModelSpec`` gains these fields, it satisfies this protocol
26
+ implicitly (structural subtyping).
27
+ """
28
+
29
+ @property
30
+ def name(self) -> str: ...
31
+ @property
32
+ def family(self) -> str: ...
33
+ @property
34
+ def provider_refs(self) -> tuple[tuple[str, str], ...]: ...
35
+ @property
36
+ def preferred_proxy(self) -> str | None: ...
37
+
38
+
39
+ _log = logging.getLogger(__name__)
40
+
41
+ # Direct workers run claude -p --bare which needs ANTHROPIC_API_KEY
42
+ _DIRECT_CREDENTIAL = "anthropic-api"
43
+
44
+ # Providers that pass model IDs through to the upstream (OpenRouter routes
45
+ # by explicit model ref regardless of the template's native family).
46
+ _PASSTHROUGH_PROVIDERS = frozenset({"openrouter"})
47
+
48
+
49
+ @dataclass(frozen=True)
50
+ class WorkerRoutingPlan:
51
+ """Pre-resolved routing for all workers in a workflow invocation.
52
+
53
+ Created once at invocation start. Frozen and passed to each worker.
54
+ ``routes`` is indexed by worker position (same order as spec list).
55
+ """
56
+
57
+ routes: tuple[RoutingResult, ...]
58
+ resolved_at: str
59
+ via_override: str | None
60
+
61
+
62
+ def resolve_model_flag(route: ModelRoute) -> str | None:
63
+ """Return the ``--model`` flag for a routed workflow worker.
64
+
65
+ Direct workers use Claude Code env pins instead of ``--model``.
66
+ Proxied workers always send an explicit model ref so ``--models``
67
+ means the same thing regardless of which compatible proxy was selected.
68
+ """
69
+ if route.provider == "direct":
70
+ return None
71
+ return route.model_ref
72
+
73
+
74
+ # ── Template metadata cache ──────────────────────────────────────
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class _TemplateMeta:
79
+ """Cached static metadata for one proxy template."""
80
+
81
+ name: str
82
+ family: str
83
+ preferred_provider: str
84
+ credentials: tuple[str, ...]
85
+
86
+
87
+ _template_cache: dict[str, _TemplateMeta] = {}
88
+
89
+
90
+ def _get_template_meta(template_name: str) -> _TemplateMeta | None:
91
+ """Load and cache template metadata (family, provider, credentials)."""
92
+ if template_name in _template_cache:
93
+ return _template_cache[template_name]
94
+
95
+ try:
96
+ import yaml
97
+
98
+ from forge.config.loader import read_template
99
+
100
+ content = read_template(template_name)
101
+ data = yaml.safe_load(content)
102
+ if not isinstance(data, dict):
103
+ return None
104
+
105
+ proxy_block = data.get("proxy", {})
106
+ if not isinstance(proxy_block, dict):
107
+ return None
108
+
109
+ family = proxy_block.get("family", "")
110
+ provider = proxy_block.get("preferred_provider", "")
111
+ if not family or not provider:
112
+ return None
113
+
114
+ from forge.core.auth.capabilities import credentials_for_template
115
+
116
+ creds = credentials_for_template(template_name)
117
+ cred_names = tuple(c.name for c in creds)
118
+
119
+ meta = _TemplateMeta(
120
+ name=template_name,
121
+ family=family,
122
+ preferred_provider=provider,
123
+ credentials=cred_names,
124
+ )
125
+ _template_cache[template_name] = meta
126
+ return meta
127
+ except Exception:
128
+ _log.debug("Could not load metadata for template '%s'", template_name, exc_info=True)
129
+ return None
130
+
131
+
132
+ def _all_template_metas() -> list[_TemplateMeta]:
133
+ """Load metadata for all available templates."""
134
+ from forge.config.loader import list_template_names
135
+
136
+ result: list[_TemplateMeta] = []
137
+ for name in list_template_names(include_internal=False):
138
+ meta = _get_template_meta(name)
139
+ if meta:
140
+ result.append(meta)
141
+ return result
142
+
143
+
144
+ def clear_template_cache() -> None:
145
+ """Clear the template metadata cache (for testing)."""
146
+ _template_cache.clear()
147
+
148
+
149
+ # ── Route derivation ─────────────────────────────────────────────
150
+
151
+
152
+ def derive_model_routes(spec: RoutableSpec) -> tuple[ModelRoute, ...]:
153
+ """Expand compact model metadata into concrete routing options.
154
+
155
+ For each provider ref on the model, inspect known proxy templates
156
+ and credential metadata to build route records. Does not inspect
157
+ the proxy registry or check running state.
158
+
159
+ Ranking (deterministic):
160
+ 1. preferred_proxy match first (if it matches a derived route)
161
+ 2. provider_refs order
162
+ 3. Native-family templates before cross-family passthrough
163
+ 4. Alphabetical template name tiebreaker
164
+ """
165
+ all_metas = _all_template_metas()
166
+ routes: list[ModelRoute] = []
167
+ preferred_routes: list[ModelRoute] = []
168
+
169
+ for provider_ns, model_ref in spec.provider_refs:
170
+ if provider_ns == "direct":
171
+ route = ModelRoute(
172
+ provider="direct",
173
+ credential=_DIRECT_CREDENTIAL,
174
+ family=spec.family,
175
+ template_id=None,
176
+ template_family=None,
177
+ model_ref=model_ref,
178
+ )
179
+ if spec.preferred_proxy is None:
180
+ preferred_routes.append(route)
181
+ else:
182
+ routes.append(route)
183
+ continue
184
+
185
+ matching = _find_matching_templates(provider_ns, spec.family, all_metas)
186
+
187
+ for meta in matching:
188
+ cred = meta.credentials[0] if meta.credentials else provider_ns
189
+ route = ModelRoute(
190
+ provider=provider_ns,
191
+ credential=cred,
192
+ family=spec.family,
193
+ template_id=meta.name,
194
+ template_family=meta.family,
195
+ model_ref=model_ref,
196
+ )
197
+ if spec.preferred_proxy and meta.name == spec.preferred_proxy:
198
+ preferred_routes.append(route)
199
+ else:
200
+ routes.append(route)
201
+
202
+ return tuple(preferred_routes + routes)
203
+
204
+
205
+ def _find_matching_templates(
206
+ provider_ns: str,
207
+ model_family: str,
208
+ all_metas: list[_TemplateMeta],
209
+ ) -> list[_TemplateMeta]:
210
+ """Find templates compatible with a provider namespace.
211
+
212
+ Returns native-family templates first, then cross-family passthrough
213
+ templates (for passthrough providers like OpenRouter), sorted
214
+ alphabetically within each group.
215
+ """
216
+ native: list[_TemplateMeta] = []
217
+ cross_family: list[_TemplateMeta] = []
218
+
219
+ for meta in all_metas:
220
+ if meta.preferred_provider != provider_ns:
221
+ continue
222
+
223
+ if meta.family == model_family:
224
+ native.append(meta)
225
+ elif provider_ns in _PASSTHROUGH_PROVIDERS:
226
+ cross_family.append(meta)
227
+
228
+ native.sort(key=lambda m: m.name)
229
+ cross_family.sort(key=lambda m: m.name)
230
+ return native + cross_family
231
+
232
+
233
+ # ── Invocation routing ───────────────────────────────────────────
234
+
235
+
236
+ def resolve_invocation_routing(
237
+ specs: Sequence[Any],
238
+ via: str | None = None,
239
+ ) -> WorkerRoutingPlan:
240
+ """Resolve routing for all workers at invocation start.
241
+
242
+ Called once by the workflow CLI command. Fail-closed: raises if
243
+ any worker has no route.
244
+ """
245
+ from forge.core.reactive.routing import resolve_subprocess_routing
246
+ from forge.core.state import now_iso
247
+
248
+ results: list[RoutingResult] = []
249
+
250
+ for spec in specs:
251
+ routes = derive_model_routes(spec)
252
+
253
+ direct_only = bool(routes) and all(r.provider == "direct" for r in routes)
254
+ if direct_only:
255
+ result = _resolve_direct_spec(spec, routes, via)
256
+ else:
257
+ result = resolve_subprocess_routing(
258
+ explicit_proxy=via,
259
+ preferred_proxy=spec.preferred_proxy,
260
+ routes=routes,
261
+ require_route=True,
262
+ advisory_check=True,
263
+ )
264
+
265
+ if result.route is None:
266
+ _raise_no_route_error(spec, routes)
267
+
268
+ _log_routing_decision(spec, result)
269
+ results.append(result)
270
+
271
+ return WorkerRoutingPlan(
272
+ routes=tuple(results),
273
+ resolved_at=now_iso(),
274
+ via_override=via,
275
+ )
276
+
277
+
278
+ def _log_routing_decision(spec: RoutableSpec, result: RoutingResult) -> None:
279
+ """Emit one consolidated line for workflow routing observability."""
280
+ route = result.route
281
+ model_ref = route.model_ref if route else "(none)"
282
+ template = result.template or (route.template_id if route else None) or "(direct)"
283
+ proxy = result.proxy_id or "(direct)"
284
+ _log.info(
285
+ "Routing decision: model=%s source=%s proxy=%s template=%s model_ref=%s",
286
+ spec.name,
287
+ result.source,
288
+ proxy,
289
+ template,
290
+ model_ref,
291
+ )
292
+
293
+
294
+ def _resolve_direct_spec(
295
+ spec: RoutableSpec,
296
+ routes: tuple[ModelRoute, ...],
297
+ via: str | None,
298
+ ) -> RoutingResult:
299
+ """Build a RoutingResult for a direct-only spec, bypassing the resolver."""
300
+ direct_route = next((r for r in routes if r.provider == "direct"), None)
301
+ if direct_route is None:
302
+ _raise_no_route_error(spec, routes)
303
+
304
+ warning = None
305
+ if via:
306
+ warning = f"Worker '{spec.name}' uses direct Anthropic routing; --proxy ignored."
307
+
308
+ return RoutingResult(
309
+ base_url=None,
310
+ proxy_id=None,
311
+ template=None,
312
+ source="direct",
313
+ route=direct_route,
314
+ credential=_DIRECT_CREDENTIAL,
315
+ warning=warning,
316
+ )
317
+
318
+
319
+ def _raise_no_route_error(spec: RoutableSpec, routes: tuple[ModelRoute, ...]) -> None:
320
+ """Raise an actionable error when no route resolves for a workflow worker.
321
+
322
+ Distinguishes "missing credential" from "credential configured but no
323
+ proxy running" to avoid sending the user to forge auth login when they
324
+ need forge proxy create/start instead.
325
+ """
326
+ try:
327
+ from forge.core.auth.capabilities import (
328
+ CREDENTIALS,
329
+ format_missing_credential_error,
330
+ )
331
+ from forge.core.auth.template_secrets import resolve_env_or_credential
332
+
333
+ if not routes:
334
+ raise RuntimeError(
335
+ f"No routes derived for model '{spec.name}' (family={spec.family}).\n"
336
+ f"Tip: Run 'forge proxy create <template>' for a compatible proxy,\n"
337
+ f" or 'forge auth login' to configure credentials."
338
+ )
339
+
340
+ cred_name = routes[0].credential
341
+ cred = CREDENTIALS.get(cred_name)
342
+
343
+ if cred:
344
+ missing_vars = [ev.name for ev in cred.env_vars if ev.required and not resolve_env_or_credential(ev.name)]
345
+ if missing_vars:
346
+ raise RuntimeError(
347
+ format_missing_credential_error(
348
+ cred,
349
+ missing_vars=missing_vars,
350
+ context=f"Workflow model '{spec.name}'",
351
+ )
352
+ )
353
+
354
+ template_ids = [r.template_id for r in routes if r.template_id]
355
+ if template_ids:
356
+ templates_str = ", ".join(template_ids[:3])
357
+ raise RuntimeError(
358
+ f"No running proxy found for model '{spec.name}'.\n"
359
+ f" Compatible templates: {templates_str}\n"
360
+ f" Tip: Run 'forge proxy create {template_ids[0]}' to create one,\n"
361
+ f" or 'forge proxy start <id>' if one exists."
362
+ )
363
+
364
+ raise RuntimeError(f"No route found for model '{spec.name}' (family={spec.family}).")
365
+ except RuntimeError:
366
+ raise
367
+ except Exception:
368
+ raise RuntimeError(f"No route found for model '{spec.name}' (family={spec.family}).")