phase-loop-runtime 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 (370) hide show
  1. phase_loop_runtime/__init__.py +20 -0
  2. phase_loop_runtime/_contract_docs/phase-loop/extraction-readiness.md +90 -0
  3. phase_loop_runtime/_contract_docs/phase-loop/granular-execution-policy.md +137 -0
  4. phase_loop_runtime/_contract_docs/phase-loop/harness-capability-matrix.md +510 -0
  5. phase_loop_runtime/_contract_docs/phase-loop/harness-skill-matrix.md +125 -0
  6. phase_loop_runtime/_contract_docs/phase-loop/harness-substrate-manifest.md +232 -0
  7. phase_loop_runtime/_contract_docs/phase-loop/pi-loop-control.md +247 -0
  8. phase_loop_runtime/_contract_docs/phase-loop/protocol.md +2759 -0
  9. phase_loop_runtime/_contract_docs/phase-loop/runtime-boundary.md +289 -0
  10. phase_loop_runtime/_contract_docs/phase-loop/spec-discovery-roots.md +47 -0
  11. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-governed-pipeline.md +42 -0
  12. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-portal-projection.md +34 -0
  13. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-regenesis.md +29 -0
  14. phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-report.md +32 -0
  15. phase_loop_runtime/_contract_docs/runtime/verification-evidence-contract.md +52 -0
  16. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/commands.txt +2 -0
  17. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/manifest.json +16 -0
  18. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/mixed.json +5 -0
  19. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/manifest.json +16 -0
  20. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/timeout-note.txt +2 -0
  21. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/manifest.json +16 -0
  22. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/redacted-summary.json +6 -0
  23. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/manifest.json +16 -0
  24. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/sample.json +7 -0
  25. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/edge.txt +2 -0
  26. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/manifest.json +16 -0
  27. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/manifest.json +16 -0
  28. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-a.txt +1 -0
  29. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-b.txt +1 -0
  30. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-c.txt +1 -0
  31. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/claimed-results.json +5 -0
  32. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/manifest.json +16 -0
  33. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/manifest.json +16 -0
  34. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/metrics.json +9 -0
  35. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/findings.md +10 -0
  36. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/manifest.json +16 -0
  37. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/manifest.json +16 -0
  38. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/scores.json +4 -0
  39. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/summary.txt +1 -0
  40. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/bundle-check.json +8 -0
  41. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/manifest.json +16 -0
  42. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/operator-note.txt +2 -0
  43. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/events-sample.jsonl +3 -0
  44. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/manifest.json +16 -0
  45. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/receipt.txt +1 -0
  46. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/evidence.json +13 -0
  47. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/manifest.json +16 -0
  48. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/summary.txt +2 -0
  49. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/manifest.json +16 -0
  50. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/matrix.json +9 -0
  51. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/notes.md +4 -0
  52. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/console-summary.txt +2 -0
  53. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/manifest.json +16 -0
  54. phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/screenshot-standin.ppm +5 -0
  55. phase_loop_runtime/adoption_bundle.py +273 -0
  56. phase_loop_runtime/advisor_board/CONTRACTS.md +257 -0
  57. phase_loop_runtime/advisor_board/__init__.py +288 -0
  58. phase_loop_runtime/advisor_board/backing.py +137 -0
  59. phase_loop_runtime/advisor_board/backing_omnigent.py +498 -0
  60. phase_loop_runtime/advisor_board/config.py +225 -0
  61. phase_loop_runtime/advisor_board/events.py +115 -0
  62. phase_loop_runtime/advisor_board/fixtures/advisor-boards.example.toml +59 -0
  63. phase_loop_runtime/advisor_board/fixtures.py +91 -0
  64. phase_loop_runtime/advisor_board/harness_mapping.py +125 -0
  65. phase_loop_runtime/advisor_board/matrix.py +146 -0
  66. phase_loop_runtime/advisor_board/observability.py +481 -0
  67. phase_loop_runtime/advisor_board/presets.py +188 -0
  68. phase_loop_runtime/advisor_board/registries.py +309 -0
  69. phase_loop_runtime/advisor_board/resolver.py +253 -0
  70. phase_loop_runtime/advisor_board/schema.py +269 -0
  71. phase_loop_runtime/advisor_board/standin.py +166 -0
  72. phase_loop_runtime/advisor_board/validation.py +159 -0
  73. phase_loop_runtime/agent_runtime_provider.py +676 -0
  74. phase_loop_runtime/baml_modular.py +656 -0
  75. phase_loop_runtime/baml_src/dotfiles_adoption_manifest.baml +37 -0
  76. phase_loop_runtime/baml_src/dotfiles_c4_document.baml +14 -0
  77. phase_loop_runtime/baml_src/dotfiles_plan_manifest.baml +50 -0
  78. phase_loop_runtime/baml_src/dotfiles_runtime_projection.baml +16 -0
  79. phase_loop_runtime/baml_src/dotfiles_task_catalog.baml +20 -0
  80. phase_loop_runtime/baml_src/emit_phase_closeout.baml +86 -0
  81. phase_loop_runtime/baml_src/evaluate_suspected_fake_evidence.baml +36 -0
  82. phase_loop_runtime/baml_src/verification_evidence.baml +40 -0
  83. phase_loop_runtime/broker.py +181 -0
  84. phase_loop_runtime/build_bundle.py +294 -0
  85. phase_loop_runtime/capability_registry.py +835 -0
  86. phase_loop_runtime/classifier.py +87 -0
  87. phase_loop_runtime/claude_agent_view.py +625 -0
  88. phase_loop_runtime/claude_channel_sidecar.py +1086 -0
  89. phase_loop_runtime/cli.py +2599 -0
  90. phase_loop_runtime/closeout.py +865 -0
  91. phase_loop_runtime/closeout_classifier.py +120 -0
  92. phase_loop_runtime/closeout_evidence_audit.py +112 -0
  93. phase_loop_runtime/closeout_validation.py +144 -0
  94. phase_loop_runtime/closeout_validators.py +203 -0
  95. phase_loop_runtime/consiliency_gates.py +374 -0
  96. phase_loop_runtime/consiliency_ingest.py +399 -0
  97. phase_loop_runtime/consiliency_layout.py +176 -0
  98. phase_loop_runtime/consiliency_scaffold.py +305 -0
  99. phase_loop_runtime/cross_repo_channel.py +331 -0
  100. phase_loop_runtime/discovery.py +2368 -0
  101. phase_loop_runtime/dispatch_lock.py +167 -0
  102. phase_loop_runtime/doc_delta_validator.py +98 -0
  103. phase_loop_runtime/docs_audit.py +285 -0
  104. phase_loop_runtime/docs_freshness.py +468 -0
  105. phase_loop_runtime/docs_surfaces.py +150 -0
  106. phase_loop_runtime/dotfiles_profile_plugin.py +96 -0
  107. phase_loop_runtime/events.py +154 -0
  108. phase_loop_runtime/events_migration.py +96 -0
  109. phase_loop_runtime/evidence_audit.py +1082 -0
  110. phase_loop_runtime/evidence_audit_config.py +172 -0
  111. phase_loop_runtime/fleet_map.py +426 -0
  112. phase_loop_runtime/fleet_metrics.py +298 -0
  113. phase_loop_runtime/fleet_metrics_export.py +196 -0
  114. phase_loop_runtime/git_discipline.py +479 -0
  115. phase_loop_runtime/git_ops.py +122 -0
  116. phase_loop_runtime/git_topology.py +252 -0
  117. phase_loop_runtime/governed_bundle.py +137 -0
  118. phase_loop_runtime/governed_premerge.py +219 -0
  119. phase_loop_runtime/governed_review.py +264 -0
  120. phase_loop_runtime/handoff.py +932 -0
  121. phase_loop_runtime/handoff_path.py +14 -0
  122. phase_loop_runtime/injection.py +740 -0
  123. phase_loop_runtime/install_status.py +88 -0
  124. phase_loop_runtime/lane_scheduler.py +317 -0
  125. phase_loop_runtime/launcher.py +2760 -0
  126. phase_loop_runtime/lease_store.py +553 -0
  127. phase_loop_runtime/maintenance.py +401 -0
  128. phase_loop_runtime/migrate_handoffs.py +201 -0
  129. phase_loop_runtime/models.py +2144 -0
  130. phase_loop_runtime/observability.py +1290 -0
  131. phase_loop_runtime/panel_invoker.py +1638 -0
  132. phase_loop_runtime/phase_loop_drift_audit.py +388 -0
  133. phase_loop_runtime/phase_worktree_executor.py +433 -0
  134. phase_loop_runtime/pipeline_adapter/__init__.py +12 -0
  135. phase_loop_runtime/pipeline_adapter/branch_ops.py +274 -0
  136. phase_loop_runtime/pipeline_adapter/flag.py +55 -0
  137. phase_loop_runtime/pipeline_adapter/markers.py +13 -0
  138. phase_loop_runtime/pipeline_adapter/merge_policy.py +72 -0
  139. phase_loop_runtime/pipeline_adapter/ratification.py +100 -0
  140. phase_loop_runtime/pipeline_adapter/sibling_matcher.py +123 -0
  141. phase_loop_runtime/plan_ir.py +523 -0
  142. phase_loop_runtime/plan_manifest.py +415 -0
  143. phase_loop_runtime/planner_validation.py +176 -0
  144. phase_loop_runtime/profiles.py +462 -0
  145. phase_loop_runtime/prompts.py +365 -0
  146. phase_loop_runtime/provenance.py +165 -0
  147. phase_loop_runtime/publishing.py +339 -0
  148. phase_loop_runtime/py.typed +1 -0
  149. phase_loop_runtime/reconcile.py +1550 -0
  150. phase_loop_runtime/redaction.py +184 -0
  151. phase_loop_runtime/reflection_sync.py +293 -0
  152. phase_loop_runtime/release_guard.py +186 -0
  153. phase_loop_runtime/render.py +432 -0
  154. phase_loop_runtime/repo_validation.py +427 -0
  155. phase_loop_runtime/review_summary.py +150 -0
  156. phase_loop_runtime/roadmap_lint.py +425 -0
  157. phase_loop_runtime/route_log.py +59 -0
  158. phase_loop_runtime/route_policy/__init__.py +11 -0
  159. phase_loop_runtime/route_policy/fixtures/route_selection.golden.json +263 -0
  160. phase_loop_runtime/runner.py +8400 -0
  161. phase_loop_runtime/runtime_paths.py +225 -0
  162. phase_loop_runtime/runtime_projection.py +113 -0
  163. phase_loop_runtime/runtime_resources.py +36 -0
  164. phase_loop_runtime/schema_export.py +292 -0
  165. phase_loop_runtime/schemas/phase_loop_closeout.schema.json +366 -0
  166. phase_loop_runtime/skill_install.py +176 -0
  167. phase_loop_runtime/skill_inventory.py +662 -0
  168. phase_loop_runtime/skill_paths.py +42 -0
  169. phase_loop_runtime/skill_sources_plugin.py +37 -0
  170. phase_loop_runtime/skills_bundle/claude-advisor-board/README.md +1 -0
  171. phase_loop_runtime/skills_bundle/claude-advisor-board/SKILL.md +38 -0
  172. phase_loop_runtime/skills_bundle/claude-advisor-panel/README.md +1 -0
  173. phase_loop_runtime/skills_bundle/claude-advisor-panel/SKILL.md +38 -0
  174. phase_loop_runtime/skills_bundle/claude-execute-detailed/README.md +1 -0
  175. phase_loop_runtime/skills_bundle/claude-execute-detailed/SKILL.md +152 -0
  176. phase_loop_runtime/skills_bundle/claude-execute-phase/README.md +1 -0
  177. phase_loop_runtime/skills_bundle/claude-execute-phase/SKILL.md +745 -0
  178. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  179. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  180. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  181. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  182. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  183. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  184. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  185. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/state.py +274 -0
  186. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  187. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/team_teardown.sh +32 -0
  188. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/validate_plan_doc.py +642 -0
  189. phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/verify_harness.sh +96 -0
  190. phase_loop_runtime/skills_bundle/claude-phase-loop/README.md +1 -0
  191. phase_loop_runtime/skills_bundle/claude-phase-loop/SKILL.md +115 -0
  192. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/README.md +1 -0
  193. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/SKILL.md +439 -0
  194. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/assets/review_prompt.md +19 -0
  195. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  196. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/roadmap-template.md +193 -0
  197. phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  198. phase_loop_runtime/skills_bundle/claude-plan-detailed/README.md +1 -0
  199. phase_loop_runtime/skills_bundle/claude-plan-detailed/SKILL.md +334 -0
  200. phase_loop_runtime/skills_bundle/claude-plan-detailed/assets/review_prompt.md +25 -0
  201. phase_loop_runtime/skills_bundle/claude-plan-phase/README.md +1 -0
  202. phase_loop_runtime/skills_bundle/claude-plan-phase/SKILL.md +776 -0
  203. phase_loop_runtime/skills_bundle/claude-plan-phase/assets/review_prompt.md +17 -0
  204. phase_loop_runtime/skills_bundle/claude-plan-phase/scripts/validate_plan_doc.py +1092 -0
  205. phase_loop_runtime/skills_bundle/claude-run-train/README.md +1 -0
  206. phase_loop_runtime/skills_bundle/claude-run-train/SKILL.md +75 -0
  207. phase_loop_runtime/skills_bundle/claude-skill-editor/README.md +1 -0
  208. phase_loop_runtime/skills_bundle/claude-skill-editor/SKILL.md +243 -0
  209. phase_loop_runtime/skills_bundle/claude-skill-editor/assets/editor_prompt.md +72 -0
  210. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/README.md +1 -0
  211. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/SKILL.md +249 -0
  212. phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  213. phase_loop_runtime/skills_bundle/claude-task-contextualizer/README.md +1 -0
  214. phase_loop_runtime/skills_bundle/claude-task-contextualizer/SKILL.md +74 -0
  215. phase_loop_runtime/skills_bundle/claude-task-contextualizer/references/task-templates.md +103 -0
  216. phase_loop_runtime/skills_bundle/codex-advisor-board/README.md +1 -0
  217. phase_loop_runtime/skills_bundle/codex-advisor-board/SKILL.md +38 -0
  218. phase_loop_runtime/skills_bundle/codex-advisor-panel/README.md +1 -0
  219. phase_loop_runtime/skills_bundle/codex-advisor-panel/SKILL.md +38 -0
  220. phase_loop_runtime/skills_bundle/codex-execute-detailed/README.md +1 -0
  221. phase_loop_runtime/skills_bundle/codex-execute-detailed/SKILL.md +154 -0
  222. phase_loop_runtime/skills_bundle/codex-execute-phase/README.md +1 -0
  223. phase_loop_runtime/skills_bundle/codex-execute-phase/SKILL.md +231 -0
  224. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  225. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  226. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  227. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  228. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  229. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  230. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  231. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/state.py +274 -0
  232. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  233. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/team_teardown.sh +32 -0
  234. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/validate_plan_doc.py +642 -0
  235. phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/verify_harness.sh +96 -0
  236. phase_loop_runtime/skills_bundle/codex-phase-loop/README.md +1 -0
  237. phase_loop_runtime/skills_bundle/codex-phase-loop/SKILL.md +248 -0
  238. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/README.md +1 -0
  239. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/SKILL.md +150 -0
  240. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/assets/review_prompt.md +19 -0
  241. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  242. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/roadmap-template.md +193 -0
  243. phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  244. phase_loop_runtime/skills_bundle/codex-plan-detailed/README.md +1 -0
  245. phase_loop_runtime/skills_bundle/codex-plan-detailed/SKILL.md +119 -0
  246. phase_loop_runtime/skills_bundle/codex-plan-detailed/assets/review_prompt.md +25 -0
  247. phase_loop_runtime/skills_bundle/codex-plan-phase/README.md +1 -0
  248. phase_loop_runtime/skills_bundle/codex-plan-phase/SKILL.md +307 -0
  249. phase_loop_runtime/skills_bundle/codex-plan-phase/assets/review_prompt.md +17 -0
  250. phase_loop_runtime/skills_bundle/codex-plan-phase/scripts/validate_plan_doc.py +1092 -0
  251. phase_loop_runtime/skills_bundle/codex-run-train/README.md +1 -0
  252. phase_loop_runtime/skills_bundle/codex-run-train/SKILL.md +75 -0
  253. phase_loop_runtime/skills_bundle/codex-skill-editor/README.md +1 -0
  254. phase_loop_runtime/skills_bundle/codex-skill-editor/SKILL.md +94 -0
  255. phase_loop_runtime/skills_bundle/codex-skill-editor/assets/editor_prompt.md +72 -0
  256. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/README.md +1 -0
  257. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/SKILL.md +114 -0
  258. phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  259. phase_loop_runtime/skills_bundle/codex-task-contextualizer/README.md +1 -0
  260. phase_loop_runtime/skills_bundle/codex-task-contextualizer/SKILL.md +102 -0
  261. phase_loop_runtime/skills_bundle/codex-task-contextualizer/references/task-templates.md +103 -0
  262. phase_loop_runtime/skills_bundle/gemini-advisor-board/README.md +1 -0
  263. phase_loop_runtime/skills_bundle/gemini-advisor-board/SKILL.md +38 -0
  264. phase_loop_runtime/skills_bundle/gemini-advisor-panel/README.md +1 -0
  265. phase_loop_runtime/skills_bundle/gemini-advisor-panel/SKILL.md +38 -0
  266. phase_loop_runtime/skills_bundle/gemini-execute-detailed/README.md +1 -0
  267. phase_loop_runtime/skills_bundle/gemini-execute-detailed/SKILL.md +144 -0
  268. phase_loop_runtime/skills_bundle/gemini-execute-phase/README.md +1 -0
  269. phase_loop_runtime/skills_bundle/gemini-execute-phase/SKILL.md +198 -0
  270. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  271. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  272. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  273. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  274. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  275. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  276. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  277. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/state.py +274 -0
  278. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  279. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/team_teardown.sh +32 -0
  280. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/validate_plan_doc.py +642 -0
  281. phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/verify_harness.sh +96 -0
  282. phase_loop_runtime/skills_bundle/gemini-phase-loop/README.md +1 -0
  283. phase_loop_runtime/skills_bundle/gemini-phase-loop/SKILL.md +111 -0
  284. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/README.md +1 -0
  285. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/SKILL.md +147 -0
  286. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/assets/review_prompt.md +19 -0
  287. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  288. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/roadmap-template.md +193 -0
  289. phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  290. phase_loop_runtime/skills_bundle/gemini-plan-detailed/README.md +1 -0
  291. phase_loop_runtime/skills_bundle/gemini-plan-detailed/SKILL.md +119 -0
  292. phase_loop_runtime/skills_bundle/gemini-plan-detailed/assets/review_prompt.md +25 -0
  293. phase_loop_runtime/skills_bundle/gemini-plan-phase/README.md +1 -0
  294. phase_loop_runtime/skills_bundle/gemini-plan-phase/SKILL.md +217 -0
  295. phase_loop_runtime/skills_bundle/gemini-plan-phase/assets/review_prompt.md +17 -0
  296. phase_loop_runtime/skills_bundle/gemini-plan-phase/scripts/validate_plan_doc.py +1092 -0
  297. phase_loop_runtime/skills_bundle/gemini-run-train/README.md +1 -0
  298. phase_loop_runtime/skills_bundle/gemini-run-train/SKILL.md +75 -0
  299. phase_loop_runtime/skills_bundle/gemini-skill-editor/README.md +1 -0
  300. phase_loop_runtime/skills_bundle/gemini-skill-editor/SKILL.md +87 -0
  301. phase_loop_runtime/skills_bundle/gemini-skill-editor/assets/editor_prompt.md +72 -0
  302. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/README.md +1 -0
  303. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/SKILL.md +109 -0
  304. phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  305. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/README.md +1 -0
  306. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/SKILL.md +98 -0
  307. phase_loop_runtime/skills_bundle/gemini-task-contextualizer/references/task-templates.md +103 -0
  308. phase_loop_runtime/skills_bundle/opencode-advisor-board/README.md +1 -0
  309. phase_loop_runtime/skills_bundle/opencode-advisor-board/SKILL.md +38 -0
  310. phase_loop_runtime/skills_bundle/opencode-advisor-panel/README.md +1 -0
  311. phase_loop_runtime/skills_bundle/opencode-advisor-panel/SKILL.md +38 -0
  312. phase_loop_runtime/skills_bundle/opencode-execute-detailed/README.md +1 -0
  313. phase_loop_runtime/skills_bundle/opencode-execute-detailed/SKILL.md +144 -0
  314. phase_loop_runtime/skills_bundle/opencode-execute-phase/README.md +1 -0
  315. phase_loop_runtime/skills_bundle/opencode-execute-phase/SKILL.md +196 -0
  316. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/allocate_worktree_name.sh +33 -0
  317. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/audit_lane_file_touches.py +197 -0
  318. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
  319. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
  320. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
  321. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
  322. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
  323. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/state.py +274 -0
  324. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
  325. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/team_teardown.sh +32 -0
  326. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/validate_plan_doc.py +642 -0
  327. phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/verify_harness.sh +96 -0
  328. phase_loop_runtime/skills_bundle/opencode-phase-loop/README.md +1 -0
  329. phase_loop_runtime/skills_bundle/opencode-phase-loop/SKILL.md +113 -0
  330. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/README.md +1 -0
  331. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/SKILL.md +147 -0
  332. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/assets/review_prompt.md +19 -0
  333. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
  334. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/roadmap-template.md +193 -0
  335. phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
  336. phase_loop_runtime/skills_bundle/opencode-plan-detailed/README.md +1 -0
  337. phase_loop_runtime/skills_bundle/opencode-plan-detailed/SKILL.md +119 -0
  338. phase_loop_runtime/skills_bundle/opencode-plan-detailed/assets/review_prompt.md +25 -0
  339. phase_loop_runtime/skills_bundle/opencode-plan-phase/README.md +1 -0
  340. phase_loop_runtime/skills_bundle/opencode-plan-phase/SKILL.md +211 -0
  341. phase_loop_runtime/skills_bundle/opencode-plan-phase/assets/review_prompt.md +17 -0
  342. phase_loop_runtime/skills_bundle/opencode-plan-phase/scripts/validate_plan_doc.py +1092 -0
  343. phase_loop_runtime/skills_bundle/opencode-run-train/README.md +1 -0
  344. phase_loop_runtime/skills_bundle/opencode-run-train/SKILL.md +75 -0
  345. phase_loop_runtime/skills_bundle/opencode-skill-editor/README.md +1 -0
  346. phase_loop_runtime/skills_bundle/opencode-skill-editor/SKILL.md +87 -0
  347. phase_loop_runtime/skills_bundle/opencode-skill-editor/assets/editor_prompt.md +72 -0
  348. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/README.md +1 -0
  349. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/SKILL.md +109 -0
  350. phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
  351. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/README.md +1 -0
  352. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/SKILL.md +98 -0
  353. phase_loop_runtime/skills_bundle/opencode-task-contextualizer/references/task-templates.md +103 -0
  354. phase_loop_runtime/state.py +156 -0
  355. phase_loop_runtime/state_degradation.py +136 -0
  356. phase_loop_runtime/state_ops.py +676 -0
  357. phase_loop_runtime/train_ledger.py +215 -0
  358. phase_loop_runtime/train_roadmap.py +339 -0
  359. phase_loop_runtime/train_runner.py +1197 -0
  360. phase_loop_runtime/verification_evidence.py +650 -0
  361. phase_loop_runtime/verification_evidence_validator.py +75 -0
  362. phase_loop_runtime/visual_evidence_validator.py +36 -0
  363. phase_loop_runtime/worker_pool.py +123 -0
  364. phase_loop_runtime/worktree_index.py +270 -0
  365. phase_loop_runtime-0.2.0.data/data/share/phase-loop-runtime/protocol/protocol.md +2475 -0
  366. phase_loop_runtime-0.2.0.dist-info/METADATA +106 -0
  367. phase_loop_runtime-0.2.0.dist-info/RECORD +370 -0
  368. phase_loop_runtime-0.2.0.dist-info/WHEEL +5 -0
  369. phase_loop_runtime-0.2.0.dist-info/entry_points.txt +9 -0
  370. phase_loop_runtime-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,253 @@
1
+ """Board resolution — name -> seats, settable default, ad-hoc seats (ABDRESOLVE lane 1).
2
+
3
+ Turns a board *reference* into a concrete ``Board`` of seats:
4
+
5
+ * a **name** (``"default"``, ``"code-review"``, ...) -> the named board from the
6
+ catalog;
7
+ * **nothing** -> the settable *default* board (bare ``advisor-board``);
8
+ * an **ad-hoc** ``--seats model:effort[:harness]`` spec -> a synthesized board.
9
+
10
+ Coded against the frozen registry/matrix interface (``registries``); the harness
11
+ lane for a bare ``model:effort`` seat is resolved via
12
+ ``CompatibilityMatrix.default_lane``. Until ABDREG ships the config loader +
13
+ presets, the catalog defaults to a **stand-in** built from the shared canonical
14
+ fixtures (``STANDIN_BOARDS``) so ABDRESOLVE resolves ``default`` and
15
+ ``code-review`` without depending on ABDREG's live output (integration at
16
+ ABDVERIFY). A caller (or ABDREG) injects the real catalog + matrix.
17
+
18
+ ``advisor-panel`` remains a working alias of the default board
19
+ (``BOARD_ALIASES``) so the historical name keeps resolving after the
20
+ advisor-panel -> advisor-board rename.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from collections.abc import Iterable, Mapping, Sequence
25
+
26
+ from .fixtures import DEFAULT_BOARD
27
+ from .registries import CompatibilityMatrix
28
+ from .schema import EFFORT_LEVELS, Board, Seat
29
+
30
+
31
+ class SeatSpecError(ValueError):
32
+ """A ``--seats model:effort[:harness]`` token is malformed."""
33
+
34
+
35
+ class BoardResolutionError(KeyError):
36
+ """A board reference names no board in the catalog (and is not an alias)."""
37
+
38
+ def __init__(self, name: str, available: Sequence[str]) -> None:
39
+ self.name = name
40
+ self.available = tuple(available)
41
+ joined = ", ".join(sorted(self.available)) or "(none)"
42
+ super().__init__(
43
+ f"unknown board {name!r}; available boards: {joined} "
44
+ "(ad-hoc seats: --seats model:effort[:harness])"
45
+ )
46
+
47
+ def __str__(self) -> str: # KeyError repr wraps the message in quotes otherwise
48
+ return self.args[0]
49
+
50
+
51
+ # The historical panel name resolves to the default premerge-review board, so
52
+ # existing agent instructions that say "advisor-panel" keep working after the
53
+ # advisor-panel -> advisor-board rename.
54
+ BOARD_ALIASES: dict[str, str] = {
55
+ "advisor-panel": "default",
56
+ "panel": "default",
57
+ }
58
+
59
+
60
+ # --- ad-hoc seat parsing ----------------------------------------------------
61
+
62
+
63
+ def parse_seat_spec(spec: str, *, matrix: CompatibilityMatrix | None = None) -> Seat:
64
+ """Parse one ``model:effort[:harness]`` token into a ``Seat``.
65
+
66
+ ``model`` required, the primary key.
67
+ ``effort`` required, a canonical level in ``EFFORT_LEVELS``.
68
+ ``harness`` optional; when omitted the lane is resolved via
69
+ ``matrix.default_lane(model)`` (frozen interface). With no matrix
70
+ the seat is left lane-unresolved (``harness=None``) for the
71
+ validation/backing phase to fill.
72
+ """
73
+ raw = (spec or "").strip()
74
+ if not raw:
75
+ raise SeatSpecError("empty seat spec; expected model:effort[:harness]")
76
+ parts = raw.split(":")
77
+ if len(parts) < 2 or len(parts) > 3:
78
+ raise SeatSpecError(
79
+ f"seat spec {spec!r} must be model:effort[:harness] (1 or 2 colons)"
80
+ )
81
+ model = parts[0].strip()
82
+ effort = parts[1].strip().lower()
83
+ harness = parts[2].strip() if len(parts) == 3 and parts[2].strip() else None
84
+ if not model:
85
+ raise SeatSpecError(f"seat spec {spec!r} has an empty model")
86
+ if effort not in EFFORT_LEVELS:
87
+ raise SeatSpecError(
88
+ f"seat spec {spec!r} effort {effort!r} not in {EFFORT_LEVELS}"
89
+ )
90
+ if harness is None and matrix is not None:
91
+ harness = matrix.default_lane(model)
92
+ return Seat(model=model, effort=effort, harness=harness)
93
+
94
+
95
+ def parse_seats(
96
+ specs: str | Iterable[str], *, matrix: CompatibilityMatrix | None = None
97
+ ) -> tuple[Seat, ...]:
98
+ """Parse a comma-separated string OR an iterable of ``model:effort[:harness]``
99
+ tokens into an ordered tuple of seats (order preserved for result re-keying)."""
100
+ if isinstance(specs, str):
101
+ tokens = [tok for tok in (t.strip() for t in specs.split(",")) if tok]
102
+ else:
103
+ tokens = [str(tok).strip() for tok in specs if str(tok).strip()]
104
+ if not tokens:
105
+ raise SeatSpecError("no seats parsed; expected model:effort[:harness], ...")
106
+ return tuple(parse_seat_spec(tok, matrix=matrix) for tok in tokens)
107
+
108
+
109
+ # --- stand-in board catalog (until ABDREG's config loader + presets) --------
110
+
111
+ # A stand-in ``code-review`` preset mirroring ``fixtures/advisor-boards.example.toml``
112
+ # so ``advisor-board --board code-review`` resolved in ABDRESOLVE before the config
113
+ # loader landed. SUPERSEDED by ``presets.PRESETS`` / ``config.load_boards`` (which
114
+ # ship the seven real presets — the live ``code-review`` is now three frontier
115
+ # adversarial seats on Fable, not this two-seat mirror); kept only as the ABDRESOLVE
116
+ # stand-in fixture, off every live path.
117
+ _STANDIN_CODE_REVIEW = Board(
118
+ name="code-review",
119
+ purpose="code-review",
120
+ seats=(
121
+ Seat(model="gpt-5.5", effort="max", harness="codex"),
122
+ Seat(model="claude-sonnet-5", effort="max", harness="claude", lens="adversarial"),
123
+ ),
124
+ )
125
+
126
+ STANDIN_BOARDS: dict[str, Board] = {
127
+ DEFAULT_BOARD.name: DEFAULT_BOARD,
128
+ _STANDIN_CODE_REVIEW.name: _STANDIN_CODE_REVIEW,
129
+ }
130
+
131
+
132
+ # --- resolver ---------------------------------------------------------------
133
+
134
+
135
+ class BoardResolver:
136
+ """Resolve a board reference to seats against an (injectable) catalog + matrix.
137
+
138
+ ``boards`` the named-board catalog; defaults to ``STANDIN_BOARDS``
139
+ (ABDREG injects the real config-loaded presets).
140
+ ``default_board`` the board a bare ``advisor-board`` resolves to (settable
141
+ via ``set_default``).
142
+ ``matrix`` frozen ``CompatibilityMatrix`` used to resolve a bare
143
+ ad-hoc seat's default lane; optional.
144
+ ``aliases`` board-name aliases (``advisor-panel`` -> ``default`` by
145
+ default) so the historical name keeps resolving.
146
+ """
147
+
148
+ def __init__(
149
+ self,
150
+ boards: Mapping[str, Board] | None = None,
151
+ *,
152
+ default_board: str = "default",
153
+ matrix: CompatibilityMatrix | None = None,
154
+ aliases: Mapping[str, str] | None = None,
155
+ ) -> None:
156
+ self._boards: dict[str, Board] = dict(boards) if boards is not None else dict(STANDIN_BOARDS)
157
+ self._aliases: dict[str, str] = dict(aliases) if aliases is not None else dict(BOARD_ALIASES)
158
+ self._matrix = matrix
159
+ self.set_default(default_board)
160
+
161
+ @property
162
+ def default_board(self) -> str:
163
+ return self._default_board
164
+
165
+ def set_default(self, name: str) -> None:
166
+ """Set the board a bare ``advisor-board`` resolves to. Fails fast if the
167
+ name (after alias) is not in the catalog."""
168
+ resolved = self._resolve_name(name)
169
+ if resolved not in self._boards:
170
+ raise BoardResolutionError(name, self.available())
171
+ self._default_board = resolved
172
+
173
+ def available(self) -> tuple[str, ...]:
174
+ """Every resolvable board name (catalog names + aliases)."""
175
+ return tuple(self._boards) + tuple(self._aliases)
176
+
177
+ def _resolve_name(self, name: str) -> str:
178
+ key = (name or "").strip()
179
+ return self._aliases.get(key, key)
180
+
181
+ def resolve(
182
+ self,
183
+ name: str | None = None,
184
+ *,
185
+ seats: str | Iterable[str] | None = None,
186
+ ) -> Board:
187
+ """Resolve to a concrete ``Board``.
188
+
189
+ * ``seats`` given -> a synthesized ad-hoc board (``--seats`` path);
190
+ * ``name`` is None/empty -> the settable default board;
191
+ * else -> the named board (alias-aware), or ``BoardResolutionError``.
192
+ """
193
+ if seats is not None:
194
+ parsed = parse_seats(seats, matrix=self._matrix)
195
+ return Board(name="ad-hoc", purpose="ad-hoc", seats=parsed)
196
+ if name is None or not str(name).strip():
197
+ return self._boards[self._default_board]
198
+ resolved = self._resolve_name(name)
199
+ try:
200
+ return self._boards[resolved]
201
+ except KeyError:
202
+ raise BoardResolutionError(name, self.available()) from None
203
+
204
+
205
+ def resolve_board(
206
+ name: str | None = None,
207
+ *,
208
+ seats: str | Iterable[str] | None = None,
209
+ boards: Mapping[str, Board] | None = None,
210
+ matrix: CompatibilityMatrix | None = None,
211
+ ) -> Board:
212
+ """One-shot convenience over ``BoardResolver`` (stand-in catalog by default)."""
213
+ return BoardResolver(boards, matrix=matrix).resolve(name, seats=seats)
214
+
215
+
216
+ # --- leg -> seat result re-keying (expressibility for same-vendor seats) -----
217
+
218
+
219
+ def seat_result_key(seat: Seat) -> str:
220
+ """The stable per-seat label a result is keyed by (``Seat.seat_key``)."""
221
+ return seat.seat_key
222
+
223
+
224
+ def key_results_by_seat(
225
+ seats: Sequence[Seat], results: Sequence["object"]
226
+ ) -> tuple[tuple[Seat, object], ...]:
227
+ """Pair a board's seats with its panel-leg results **by position**.
228
+
229
+ The v4 ``PanelLegResult.leg`` keys results by vendor, so a board with two
230
+ same-vendor seats (e.g. two openai seats on ``codex`` and ``opencode``) could
231
+ not be told apart. Pairing by position, with ``seat.seat_key`` as the label,
232
+ makes two same-vendor seats distinctly expressible. ABDHOME wires the actual
233
+ per-seat spawn; ABDRESOLVE freezes the position<->seat_key identity.
234
+ """
235
+ if len(seats) != len(results):
236
+ raise ValueError(
237
+ f"seat/result count mismatch: {len(seats)} seats vs {len(results)} results"
238
+ )
239
+ return tuple((seat, result) for seat, result in zip(seats, results))
240
+
241
+
242
+ __all__ = [
243
+ "SeatSpecError",
244
+ "BoardResolutionError",
245
+ "BOARD_ALIASES",
246
+ "STANDIN_BOARDS",
247
+ "BoardResolver",
248
+ "resolve_board",
249
+ "parse_seat_spec",
250
+ "parse_seats",
251
+ "seat_result_key",
252
+ "key_results_by_seat",
253
+ ]
@@ -0,0 +1,269 @@
1
+ """Advisor Board — frozen seat/board schema + vendor projection (IF-0-ABDFREEZE-1, -4).
2
+
3
+ This module is the **interface freeze** for the model-first Advisor Board. It is
4
+ additive and behavior-neutral: it introduces no runtime path and does not touch
5
+ `panel_invoker`. Downstream phases (ABDREG / ABDRESOLVE / ABDHOME) code against
6
+ the typed structures frozen here so the fan-out integrates without a big-bang.
7
+
8
+ The seat model inverts the v4 panel: a **seat is a cognition** — `{model, effort,
9
+ harness?, lens?, auth?}` — where the harness is a defaulted-but-overridable
10
+ execution *lane*, not the primary key. A **board** is a named, purpose-tagged,
11
+ open-ended list of seats.
12
+
13
+ Two freezes here are load-bearing for back-compat and must reproduce today's
14
+ behavior exactly (proven by ``tests/test_advisor_board_schema.py`` and
15
+ ``tests/test_advisor_board_backcompat.py``):
16
+
17
+ * **Seat -> vendor-family projection** (``vendor_family`` / ``seat_vendor_family``)
18
+ must be byte-consistent with the existing governed-gate logic
19
+ (`governed_review.author_vendor_for_model` :60-75 and
20
+ `governed_review._EXECUTOR_VENDOR` :47-53). The governed reviewer != author
21
+ disjointness keys on vendor identity; model-first breaks the ``leg == vendor``
22
+ assumption, so ABDHOME rewires `governed_review` / `governed_premerge` to
23
+ consume *this* canonical projection. If the projection drifts, custom boards
24
+ silently corrupt reviewer-disjointness.
25
+
26
+ * **Host-leg identity** — which seat is the native in-process host leg when the
27
+ board runs *inside* a harness (e.g. Claude Code -> native ``Agent``) versus the
28
+ homebrew subprocess legs. Today the standalone Python runner spawns every leg
29
+ as a subprocess (no host leg), so the additive ``Seat.host_leg`` marker
30
+ defaults ``False`` and ``identify_host_leg`` returns ``None`` for the default
31
+ board — today's behavior, untouched.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ import os
36
+ from dataclasses import dataclass
37
+ from pathlib import Path
38
+
39
+
40
+ # --- canonical vocabularies -------------------------------------------------
41
+
42
+ # Model-first, harness-agnostic effort ladder. Each harness translates a canonical
43
+ # level to its own invocation form (see ``harness_mapping``). Ordered low -> high.
44
+ EFFORT_LEVELS: tuple[str, ...] = ("low", "medium", "high", "max")
45
+
46
+ # Credential lane for a seat. Subscription is the default lane (Assumptions §);
47
+ # ``api_key`` is only reachable behind an explicit board-level opt-in
48
+ # (``Board.allow_api_key_fallback``) and injects ONLY the seat vendor's key.
49
+ AUTH_SUBSCRIPTION = "subscription"
50
+ AUTH_API_KEY = "api_key"
51
+ AUTH_LANES: tuple[str, ...] = (AUTH_SUBSCRIPTION, AUTH_API_KEY)
52
+
53
+ # Provider transport backing a seat. ``homebrew`` keeps the built-3 + the native
54
+ # host leg (ABDHOME); ``omnigent`` supplies breadth via omniagent-plus -> Omnigent
55
+ # (ABDOMNI). Default homebrew keeps the default board all-homebrew.
56
+ BACKING_HOMEBREW = "homebrew"
57
+ BACKING_OMNIGENT = "omnigent"
58
+ PROVIDER_BACKINGS: tuple[str, ...] = (BACKING_HOMEBREW, BACKING_OMNIGENT)
59
+
60
+ # Board config format + location (IF-0-ABDFREEZE-1). Honors ``XDG_CONFIG_HOME``.
61
+ CONFIG_RELATIVE_PATH = "agent-harness/advisor-boards.toml"
62
+
63
+
64
+ def board_config_path(env: "os._Environ[str] | dict[str, str] | None" = None) -> Path:
65
+ """Frozen config location: ``$XDG_CONFIG_HOME/agent-harness/advisor-boards.toml``
66
+ (default ``~/.config/agent-harness/advisor-boards.toml``). The loader lands in
67
+ ABDREG; this only freezes *where* it reads."""
68
+ src = os.environ if env is None else env
69
+ base = src.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
70
+ return Path(base) / CONFIG_RELATIVE_PATH
71
+
72
+
73
+ # --- vendor-family projection (governed reviewer != author, model-first) -----
74
+
75
+ # Harness -> vendor family. Mirrors `governed_review._EXECUTOR_VENDOR` (:47-53)
76
+ # EXACTLY so the projection reproduces today's executor->vendor mapping. Two
77
+ # same-vendor seats on different harnesses (gpt-5.5 on ``codex`` and on
78
+ # ``opencode``) MUST project to the same family, which this table encodes.
79
+ _HARNESS_VENDOR: dict[str, str] = {
80
+ "codex": "codex",
81
+ "opencode": "codex", # openai-family models
82
+ "claude": "claude",
83
+ "gemini": "gemini",
84
+ "pi": "pi",
85
+ }
86
+
87
+
88
+ def vendor_of_harness(harness: str) -> str:
89
+ """Harness (execution lane) -> vendor family, with an identity fallback for an
90
+ unknown harness. Byte-consistent with
91
+ `governed_review.author_vendor_for_executor`."""
92
+ h = (harness or "").lower()
93
+ return _HARNESS_VENDOR.get(h, h)
94
+
95
+
96
+ def vendor_of_model(model: str) -> str:
97
+ """Concrete model id -> vendor family, or ``""`` when the model is inconclusive.
98
+
99
+ The matched branches reproduce `governed_review.author_vendor_for_model`
100
+ (:69-74) EXACTLY. The one intentional difference: this returns ``""`` (rather
101
+ than the raw model) for an unmatched model, so ``vendor_family`` can then fall
102
+ back to the harness lane. ``author_vendor_for_model``-equivalence for a bare
103
+ model is recovered as ``vendor_of_model(m) or m.lower()`` and is asserted in
104
+ the schema test.
105
+ """
106
+ m = (model or "").lower()
107
+ if not m:
108
+ return ""
109
+ if "claude" in m or "opus" in m or "sonnet" in m or "haiku" in m:
110
+ return "claude"
111
+ if "gemini" in m or m in {"pro", "flash", "flash-lite", "auto"}:
112
+ return "gemini"
113
+ if m.startswith("gpt") or m.startswith("o1") or m.startswith("o3") or m.startswith("openai/"):
114
+ return "codex" # the codex panel leg runs the openai-family model
115
+ return ""
116
+
117
+
118
+ def vendor_family(model: str, harness: str | None = None) -> str:
119
+ """Canonical model-first seat -> vendor-family projection.
120
+
121
+ Model wins when conclusive; otherwise the harness lane decides; otherwise the
122
+ lowercased model is the family (matching the historical bare-model fallback).
123
+ This is THE canonical projection ABDHOME rewires the governed gates onto — not
124
+ a parallel copy — so reviewer != author disjointness survives model-first.
125
+ """
126
+ fam = vendor_of_model(model)
127
+ if fam:
128
+ return fam
129
+ if harness:
130
+ return vendor_of_harness(harness)
131
+ return (model or "").lower()
132
+
133
+
134
+ # --- seat + board -----------------------------------------------------------
135
+
136
+
137
+ @dataclass(frozen=True)
138
+ class Seat:
139
+ """A single cognition on a board (model-first).
140
+
141
+ ``model`` concrete model id (the primary key). Required.
142
+ ``effort`` canonical effort level (``EFFORT_LEVELS``). Required — the
143
+ model-registry default is applied by the resolver (ABDRESOLVE),
144
+ not silently here.
145
+ ``harness`` execution lane; ``None`` -> ``default_lane(model)`` at resolution.
146
+ ``lens`` optional review persona/lens (advisory framing); no behavior yet.
147
+ ``auth`` credential lane (``AUTH_LANES``); subscription by default.
148
+ ``backing`` provider transport (``PROVIDER_BACKINGS``); homebrew by default.
149
+ ``host_leg`` additive marker: this seat is the native in-process host leg
150
+ (not a subprocess). Default ``False`` == today's subprocess leg.
151
+ """
152
+
153
+ model: str
154
+ effort: str
155
+ harness: str | None = None
156
+ lens: str | None = None
157
+ auth: str = AUTH_SUBSCRIPTION
158
+ backing: str = BACKING_HOMEBREW
159
+ host_leg: bool = False
160
+
161
+ def __post_init__(self) -> None:
162
+ if not self.model or not str(self.model).strip():
163
+ raise ValueError("seat.model is required")
164
+ if self.effort not in EFFORT_LEVELS:
165
+ raise ValueError(f"seat.effort {self.effort!r} not in {EFFORT_LEVELS}")
166
+ if self.auth not in AUTH_LANES:
167
+ raise ValueError(f"seat.auth {self.auth!r} not in {AUTH_LANES}")
168
+ if self.backing not in PROVIDER_BACKINGS:
169
+ raise ValueError(f"seat.backing {self.backing!r} not in {PROVIDER_BACKINGS}")
170
+
171
+ @property
172
+ def vendor_family(self) -> str:
173
+ """This seat's vendor family (model-first projection)."""
174
+ return vendor_family(self.model, self.harness)
175
+
176
+ @property
177
+ def seat_key(self) -> str:
178
+ """Stable per-seat LABEL for result re-keying (ABDRESOLVE re-keys
179
+ ``PanelLegResult.leg`` -> seat so two same-vendor seats are expressible).
180
+
181
+ Incorporates every user-distinguishing field — (harness-lane, model,
182
+ effort, lens) — so seats that differ only by ``lens`` (a natural
183
+ brainstorm board: same model, adversarial vs supportive) get distinct
184
+ keys. This is a LABEL, not a guaranteed-unique id: a board may still hold
185
+ two byte-identical seats, so ABDRESOLVE MUST key results by **seat
186
+ position** and use ``seat_key`` only as the human-readable label. Frozen
187
+ this way so the fan-out builds on a stable, collision-aware contract.
188
+ """
189
+ lane = self.harness or f"@{vendor_family(self.model)}"
190
+ base = f"{lane}:{self.model}:{self.effort}"
191
+ return f"{base}:{self.lens}" if self.lens else base
192
+
193
+
194
+ @dataclass(frozen=True)
195
+ class Board:
196
+ """A named, purpose-tagged, open-ended list of seats.
197
+
198
+ ``allow_api_key_fallback`` defaults ``False`` — no seat may use the api-key
199
+ lane unless the board explicitly opts in (never-silent-key, IF-0-ABDFREEZE-3).
200
+ """
201
+
202
+ name: str
203
+ purpose: str
204
+ seats: tuple[Seat, ...]
205
+ allow_api_key_fallback: bool = False
206
+
207
+ def __post_init__(self) -> None:
208
+ if not self.name or not str(self.name).strip():
209
+ raise ValueError("board.name is required")
210
+ if not isinstance(self.seats, tuple):
211
+ # keep the board hashable/frozen: seats must be a tuple of Seat
212
+ raise TypeError("board.seats must be a tuple of Seat")
213
+ for seat in self.seats:
214
+ if not isinstance(seat, Seat):
215
+ raise TypeError(f"board.seats entries must be Seat, got {type(seat)!r}")
216
+ if not self.allow_api_key_fallback:
217
+ offenders = [s for s in self.seats if s.auth == AUTH_API_KEY]
218
+ if offenders:
219
+ raise ValueError(
220
+ "board has api_key seats but allow_api_key_fallback is False "
221
+ "(never-silent-key): "
222
+ + ", ".join(s.seat_key for s in offenders)
223
+ )
224
+
225
+ def seat_vendor_families(self) -> tuple[str, ...]:
226
+ return tuple(seat.vendor_family for seat in self.seats)
227
+
228
+
229
+ def seat_vendor_family(seat: Seat) -> str:
230
+ """Free-function form of ``Seat.vendor_family`` (the projection ABDHOME calls
231
+ when it holds a seat but not the property)."""
232
+ return vendor_family(seat.model, seat.harness)
233
+
234
+
235
+ # --- host-leg identity ------------------------------------------------------
236
+
237
+
238
+ @dataclass(frozen=True)
239
+ class HostContext:
240
+ """The runtime host the board is currently running *inside*.
241
+
242
+ ``host_harness`` is the harness whose process is hosting the board (e.g.
243
+ ``"claude"`` when the board runs inside Claude Code and can reach the native
244
+ in-process ``Agent`` tool). ``None`` == the standalone Python runner, which
245
+ spawns every leg as a subprocess — TODAY'S DEFAULT, no host leg.
246
+ """
247
+
248
+ host_harness: str | None = None
249
+
250
+
251
+ def identify_host_leg(board: Board, host: HostContext | None = None) -> Seat | None:
252
+ """Return the seat that IS the native host leg for ``host``, or ``None``.
253
+
254
+ A seat is the host leg iff (a) the board runs inside a harness
255
+ (``host.host_harness`` set), and (b) the seat's resolved lane matches that
256
+ harness, and (c) the seat is marked ``host_leg`` OR it is the first seat on
257
+ the hosting harness (auto-identification for a board that predates the
258
+ marker). For the standalone runner (``host_harness is None``) this returns
259
+ ``None`` — every leg is a subprocess, exactly as today. The native host leg,
260
+ once identified, must never be routed through the gateway (ABDHOME invariant).
261
+ """
262
+ if host is None or not host.host_harness:
263
+ return None
264
+ hosting = host.host_harness.lower()
265
+ on_host = [s for s in board.seats if (s.harness or "").lower() == hosting]
266
+ if not on_host:
267
+ return None
268
+ explicit = [s for s in on_host if s.host_leg]
269
+ return explicit[0] if explicit else on_host[0]
@@ -0,0 +1,166 @@
1
+ """Fixture-backed stand-in registries + matrix (ABDRESOLVE / ABDHOME test support).
2
+
3
+ ABDREG (Phase 2) populates the *real* six-harness registry, model registry, and
4
+ ``(model x harness)`` compatibility matrix behind the frozen Protocols in
5
+ ``registries``. Until then, ABDRESOLVE codes against those Protocols using a
6
+ stand-in built **from the shared canonical fixtures** (``fixtures.py``) — the
7
+ ABDFREEZE anti-divergence keystone. Because the stand-in is derived from the same
8
+ ``CANONICAL_VALID_PAIRS`` / ``CANONICAL_INVALID_PAIRS`` / ``DEFAULT_SEATS`` that
9
+ ABDREG populates from, the resolver/validation logic verified here integrates with
10
+ ABDREG's real data at ABDVERIFY without a mock-vs-real cliff.
11
+
12
+ This module is deliberately thin and additive: it introduces no running path and
13
+ is imported only by the resolver's default catalog and by tests. ABDREG replaces
14
+ these with populated implementations; ABDRESOLVE never depends on ABDREG's live
15
+ output (integration is at ABDVERIFY).
16
+ """
17
+ from __future__ import annotations
18
+
19
+ from dataclasses import dataclass
20
+
21
+ from .fixtures import (
22
+ CANONICAL_INVALID_PAIRS,
23
+ CANONICAL_VALID_PAIRS,
24
+ DEFAULT_SEATS,
25
+ )
26
+ from .registries import (
27
+ AuthAvailability,
28
+ HarnessSpec,
29
+ MatrixVerdict,
30
+ ModelSpec,
31
+ )
32
+ from .schema import (
33
+ AUTH_SUBSCRIPTION,
34
+ BACKING_HOMEBREW,
35
+ vendor_family,
36
+ vendor_of_harness,
37
+ vendor_of_model,
38
+ )
39
+
40
+
41
+ # The default lane a bare ``model`` seat resolves onto, derived from the canonical
42
+ # default seats (the built-3) plus the same-vendor-across-harness valid pairs. This
43
+ # is the stand-in for ``ModelRegistry.default_lane`` / ``CompatibilityMatrix.default_lane``.
44
+ _DEFAULT_LANE: dict[str, str] = {seat.model: seat.harness or "" for seat in DEFAULT_SEATS}
45
+
46
+
47
+ def _default_lane_for(model: str) -> str:
48
+ lane = _DEFAULT_LANE.get(model)
49
+ if lane:
50
+ return lane
51
+ # Fall back to the first canonical valid lane for the model, else the vendor family.
52
+ for pair_model, pair_harness in CANONICAL_VALID_PAIRS:
53
+ if pair_model == model:
54
+ return pair_harness
55
+ return vendor_family(model)
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class StandinCompatibilityMatrix:
60
+ """A ``CompatibilityMatrix`` (frozen Protocol) backed by the canonical fixtures.
61
+
62
+ ``is_valid`` accepts every ``CANONICAL_VALID_PAIRS`` entry (and any
63
+ same-vendor-family pairing derivable from them), rejects every
64
+ ``CANONICAL_INVALID_PAIRS`` entry, and rejects an unknown pairing as a
65
+ cross-vendor mismatch. The returned ``AuthAvailability`` reports the
66
+ subscription lane available for a valid pair (the default lane) so a
67
+ valid-but-unauthed seat is *expressible* distinctly from an invalid pairing.
68
+ """
69
+
70
+ def is_valid(self, model: str, harness: str) -> MatrixVerdict:
71
+ pair = (model, harness)
72
+ if pair in CANONICAL_INVALID_PAIRS:
73
+ return (False, AuthAvailability(detail=f"{model} does not run on the {harness} lane"))
74
+ if pair in CANONICAL_VALID_PAIRS:
75
+ return (True, AuthAvailability(subscription=True, detail="subscription"))
76
+ # Not enumerated: valid iff the model's vendor family matches the harness
77
+ # lane's family (the same rule the canonical pairs encode) — so gpt-5.5 on
78
+ # any openai-family lane is valid, gpt-5.5 on claude is not.
79
+ model_fam = vendor_of_model(model)
80
+ lane_fam = vendor_of_harness(harness)
81
+ if model_fam and model_fam == lane_fam:
82
+ return (True, AuthAvailability(subscription=True, detail="subscription"))
83
+ return (
84
+ False,
85
+ AuthAvailability(detail=f"{model} is not compatible with the {harness} lane"),
86
+ )
87
+
88
+ def default_lane(self, model: str) -> str:
89
+ return _default_lane_for(model)
90
+
91
+
92
+ @dataclass(frozen=True)
93
+ class StandinModelRegistry:
94
+ """A ``ModelRegistry`` (frozen Protocol) backed by the canonical fixtures.
95
+
96
+ ``runnable_by`` lists every canonical lane a model is valid on — used by
97
+ ``validation`` to render an actionable "did you mean" diagnostic on an invalid
98
+ seat.
99
+ """
100
+
101
+ def list_models(self) -> tuple[ModelSpec, ...]:
102
+ return tuple(self.get(model) for model in _canonical_models())
103
+
104
+ def get(self, model: str) -> ModelSpec:
105
+ lanes = runnable_lanes(model)
106
+ return ModelSpec(
107
+ model=model,
108
+ vendor_family=vendor_family(model),
109
+ default_lane=_default_lane_for(model),
110
+ runnable_by=lanes,
111
+ )
112
+
113
+ def default_lane(self, model: str) -> str:
114
+ return _default_lane_for(model)
115
+
116
+
117
+ @dataclass(frozen=True)
118
+ class StandinHarnessRegistry:
119
+ """A minimal ``HarnessRegistry`` over the canonical lanes (probe-agnostic)."""
120
+
121
+ def list_harnesses(self) -> tuple[HarnessSpec, ...]:
122
+ return tuple(self.get(name) for name in _canonical_harnesses())
123
+
124
+ def get(self, name: str) -> HarnessSpec:
125
+ return HarnessSpec(
126
+ name=name,
127
+ cli=name,
128
+ auth_lanes=(AUTH_SUBSCRIPTION,),
129
+ backing=BACKING_HOMEBREW,
130
+ )
131
+
132
+ def is_available(self, name: str) -> bool:
133
+ return name in _canonical_harnesses()
134
+
135
+
136
+ def runnable_lanes(model: str) -> tuple[str, ...]:
137
+ """The canonical harness lanes a ``model`` is valid on (fixture-derived)."""
138
+ lanes: list[str] = []
139
+ for pair_model, pair_harness in CANONICAL_VALID_PAIRS:
140
+ if pair_model == model and pair_harness not in lanes:
141
+ lanes.append(pair_harness)
142
+ return tuple(lanes)
143
+
144
+
145
+ def _canonical_models() -> tuple[str, ...]:
146
+ seen: list[str] = []
147
+ for model, _ in CANONICAL_VALID_PAIRS:
148
+ if model not in seen:
149
+ seen.append(model)
150
+ return tuple(seen)
151
+
152
+
153
+ def _canonical_harnesses() -> tuple[str, ...]:
154
+ seen: list[str] = []
155
+ for _, harness in CANONICAL_VALID_PAIRS:
156
+ if harness not in seen:
157
+ seen.append(harness)
158
+ return tuple(seen)
159
+
160
+
161
+ __all__ = [
162
+ "StandinCompatibilityMatrix",
163
+ "StandinModelRegistry",
164
+ "StandinHarnessRegistry",
165
+ "runnable_lanes",
166
+ ]