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,146 @@
1
+ """(model x harness) compatibility + auth-availability matrix + config-time
2
+ validation (ABDREG, Phase 2 — lane 3).
3
+
4
+ Populates the frozen ``CompatibilityMatrix`` interface (registries.py:101-115):
5
+
6
+ * ``is_valid(model, harness) -> (ok, AuthAvailability)`` — ``ok`` is whether the
7
+ pairing is *expressible at all* (an ``openai``-family model on the ``claude``
8
+ lane is not); ``AuthAvailability`` reports which credential lanes are actually
9
+ usable so a valid-but-unauthed seat degrades (skip-with-warning) rather than
10
+ blocking. ``is_valid`` is TOTAL — it never raises, returning ``(False, …)`` with
11
+ a ``detail`` for an unknown/incompatible pairing.
12
+ * ``default_lane(model)`` — the harness a bare ``model`` seat resolves onto,
13
+ delegated to the model registry.
14
+
15
+ Validity is model-first: a pairing is valid iff the harness's lane vendor-family
16
+ matches the model's vendor-family (the FROZEN ``schema.vendor_family`` projection
17
+ that also keeps the governed reviewer≠author disjointness intact). That makes
18
+ ``gpt-5.5`` valid on both ``codex`` and ``opencode`` (same family) but invalid on
19
+ ``claude`` — exactly ``fixtures.CANONICAL_VALID_PAIRS`` /
20
+ ``CANONICAL_INVALID_PAIRS``.
21
+
22
+ The CONFIG-TIME gate (``validate_seat`` / ``validate_board``) lives in
23
+ ``validation.py`` — the single canonical seat-validation API. It resolves a
24
+ seat's lane (``harness or default_lane(model)``), rejects an invalid pairing or an
25
+ over-ceiling effort with an actionable message, and is what the config loader
26
+ (``config.py``) runs on every board — presets included.
27
+ """
28
+ from __future__ import annotations
29
+
30
+ import os
31
+ from collections.abc import Callable, Mapping
32
+ from dataclasses import dataclass, field
33
+
34
+ from .backing import VENDOR_API_KEY_VARS
35
+ from .registries import (
36
+ DEFAULT_HARNESS_REGISTRY,
37
+ DEFAULT_MODEL_REGISTRY,
38
+ AuthAvailability,
39
+ HarnessRegistry,
40
+ MatrixVerdict,
41
+ ModelRegistry,
42
+ UnknownHarnessError,
43
+ UnknownModelError,
44
+ )
45
+ from .schema import (
46
+ AUTH_API_KEY,
47
+ AUTH_SUBSCRIPTION,
48
+ vendor_family,
49
+ vendor_of_harness,
50
+ )
51
+
52
+
53
+ @dataclass
54
+ class DefaultCompatibilityMatrix:
55
+ """Populated ``CompatibilityMatrix`` over the six-harness / model registries.
56
+
57
+ ``env`` (default ``os.environ``) and ``probe`` (default the harness
58
+ registry's PATH probe) are injectable so auth-availability is deterministic in
59
+ tests: the api-key lane is reported available ONLY when the vendor's key var
60
+ is actually present in ``env`` (so no-silent-key is testable, not asserted),
61
+ and the subscription lane ONLY when the harness CLI is on PATH.
62
+ """
63
+
64
+ harnesses: HarnessRegistry = field(default_factory=lambda: DEFAULT_HARNESS_REGISTRY)
65
+ models: ModelRegistry = field(default_factory=lambda: DEFAULT_MODEL_REGISTRY)
66
+ env: Mapping[str, str] | None = None
67
+
68
+ def _env(self) -> Mapping[str, str]:
69
+ return os.environ if self.env is None else self.env
70
+
71
+ def default_lane(self, model: str) -> str:
72
+ # Delegates to the model registry, which raises UnknownModelError (with
73
+ # the known-model list) for an unregistered model — the unknown-model
74
+ # config-time rejection.
75
+ return self.models.default_lane(model)
76
+
77
+ def _auth_availability(self, model: str, harness_spec) -> AuthAvailability:
78
+ env = self._env()
79
+ sub = AUTH_SUBSCRIPTION in harness_spec.auth_lanes and self.harnesses.is_available(
80
+ harness_spec.name
81
+ )
82
+ vendor = vendor_family(model, harness_spec.name)
83
+ key_vars = VENDOR_API_KEY_VARS.get(vendor, ())
84
+ api = (
85
+ AUTH_API_KEY in harness_spec.auth_lanes
86
+ and bool(key_vars)
87
+ and any(var in env for var in key_vars)
88
+ )
89
+ bits = []
90
+ if sub:
91
+ bits.append("subscription")
92
+ if api:
93
+ bits.append("api_key")
94
+ detail = ("usable lanes: " + ", ".join(bits)) if bits else (
95
+ f"no usable auth lane for {model!r} on {harness_spec.name!r} "
96
+ "(no subscription CLI on PATH, no vendor api key) — seat will skip-with-warning"
97
+ )
98
+ return AuthAvailability(subscription=sub, api_key=api, detail=detail)
99
+
100
+ def is_valid(self, model: str, harness: str) -> MatrixVerdict:
101
+ try:
102
+ self.models.get(model)
103
+ except UnknownModelError as exc:
104
+ return (False, AuthAvailability(detail=str(exc)))
105
+ try:
106
+ harness_spec = self.harnesses.get(harness)
107
+ except UnknownHarnessError as exc:
108
+ return (False, AuthAvailability(detail=str(exc)))
109
+ model_family = vendor_family(model, harness)
110
+ lane_family = vendor_of_harness(harness)
111
+ if model_family != lane_family:
112
+ valid_lanes = ", ".join(self.models.get(model).runnable_by) or "(none)"
113
+ detail = (
114
+ f"model {model!r} ({model_family} family) cannot run on harness "
115
+ f"{harness!r} ({lane_family} family); valid harness lanes for "
116
+ f"{model!r}: {valid_lanes}"
117
+ )
118
+ return (False, AuthAvailability(detail=detail))
119
+ return (True, self._auth_availability(model, harness_spec))
120
+
121
+
122
+ def default_matrix(
123
+ *,
124
+ env: Mapping[str, str] | None = None,
125
+ probe: Callable[[str], bool] | None = None,
126
+ ) -> DefaultCompatibilityMatrix:
127
+ """The matrix over the default six-harness / model registries."""
128
+ from .registries import DefaultHarnessRegistry
129
+
130
+ harnesses = DefaultHarnessRegistry(probe=probe) if probe is not None else DEFAULT_HARNESS_REGISTRY
131
+ return DefaultCompatibilityMatrix(harnesses=harnesses, models=DEFAULT_MODEL_REGISTRY, env=env)
132
+
133
+
134
+ # Config-time seat/board validation lives in ``validation.py`` (the single
135
+ # canonical API — ``validate_seat`` / ``validate_board`` / ``SeatValidationError``
136
+ # / ``SeatVerdict``). This module owns only the matrix itself: ``is_valid`` and
137
+ # ``default_lane`` (the frozen ``CompatibilityMatrix`` interface) plus the
138
+ # populated ``DefaultCompatibilityMatrix`` / ``default_matrix`` factory. The
139
+ # effort-ceiling check that used to live here is folded into that canonical
140
+ # ``validate_seat`` (which reads the ceiling off this matrix's ``.models``).
141
+
142
+
143
+ __all__ = [
144
+ "DefaultCompatibilityMatrix",
145
+ "default_matrix",
146
+ ]
@@ -0,0 +1,481 @@
1
+ """ABDOBS — observability forwarding: envelope → state-ledger sink + async
2
+ best-effort dispatch (Phase 6, specs/phase-plans-v5.md).
3
+
4
+ This wires the MAPPING that ABDFREEZE-5 deferred: the frozen internal
5
+ ``AdvisorBoardEvent`` envelope (:mod:`events`) → omniagent-plus's own
6
+ ``state-ledger`` / ``ui-read-model`` — the **confirmed** sink, because v0.4.0's
7
+ HTTP surface is launcher-centric and exposes **no ingestion endpoint** for an
8
+ externally-launched (native) session. We control that ledger, so the mapping is
9
+ ours to own here.
10
+
11
+ Three load-bearing rules carry over from ABDFREEZE-5 and are enforced here:
12
+
13
+ * **launcher ≠ observability-plane** — a natively-launched leg stays natively
14
+ launched and only *emits*; nothing in this module can create a session or send
15
+ a turn (the sinks are structurally emit-only), so observability can never
16
+ relaunch the native host leg through the gateway.
17
+ * **async / best-effort, never delays or fails the native leg** —
18
+ :class:`AsyncForwardingSink` enqueues with a NON-BLOCKING put on the leg's
19
+ thread and does the (possibly slow / failing) sink write on a background
20
+ daemon thread; :class:`BoardObserver` wraps construct+map+enqueue in a
21
+ swallow-all so even a bad event kind or a full queue never touches the leg.
22
+ * **our envelope, not a guessed upstream schema** — the wire target is
23
+ omniagent-plus's *own* frozen shapes (``runtime_event.v0.1`` inside
24
+ ``state_ledger_record.v0.1``), so a real binding can feed
25
+ ``AuditLedger.appendRuntimeEvent`` verbatim.
26
+
27
+ **Cross-language seam.** The ledger is TypeScript (omniagent-plus
28
+ ``packages/state-ledger``); the emit is Python (here). This module therefore
29
+ ships (a) the pure mapping functions, (b) a :class:`LedgerWriter` adapter
30
+ Protocol — the documented integration boundary a real omniagent-plus binding
31
+ implements over IPC/HTTP/a shared file — and (c) :class:`JsonlLedgerWriter`, a
32
+ reference transport that appends the exact ``state_ledger_record.v0.1`` records
33
+ the TS ``AppendOnlyStore`` ingests. It does NOT reimplement ledger internals
34
+ (retention / replay / compaction stay TS-side).
35
+ """
36
+ from __future__ import annotations
37
+
38
+ import json
39
+ import queue
40
+ import threading
41
+ import uuid
42
+ from collections.abc import Mapping
43
+ from datetime import datetime, timezone
44
+ from pathlib import Path
45
+ from typing import Any, Protocol, runtime_checkable
46
+
47
+ from .events import (
48
+ AdvisorBoardEvent,
49
+ EventSink,
50
+ best_effort_forward,
51
+ )
52
+
53
+ # --- per-workload boundary (documented + structurally enforced) -------------
54
+ # Board = native launch + OPTIONAL forward (this module); phase-execution =
55
+ # Omnigent-as-launcher (CS-2.2, out of scope). The native host leg is NEVER
56
+ # relaunched through the gateway for observability's sake — the sinks below are
57
+ # emit-only, so this path *cannot* launch anything.
58
+ WORKLOAD_BOARD = "advisor_board"
59
+ WORKLOAD_PHASE_EXECUTION = "phase_execution"
60
+
61
+ # omniagent-plus wire schemas we target (owned by us; frozen upstream).
62
+ LEDGER_RECORD_SCHEMA = "state_ledger_record.v0.1" # packages/core-contracts state-ledger.ts
63
+ RUNTIME_EVENT_SCHEMA = "runtime_event.v0.1" # packages/core-contracts events.ts
64
+ RUNTIME_FAILURE_SCHEMA = "runtime_failure.v0.1" # packages/core-contracts errors.ts
65
+ LEDGER_RECORD_KIND = "runtime_event" # AuditLedger.appendRuntimeEvent kind
66
+
67
+ # Our envelope kind -> the omniagent runtime.* event type it maps to. A board run
68
+ # projects to a session; each seat projects to a turn within that session.
69
+ _KIND_TO_RUNTIME_TYPE: dict[str, str] = {
70
+ "board.started": "runtime.session.created",
71
+ "board.completed": "runtime.session.closed",
72
+ "seat.started": "runtime.turn.started",
73
+ "seat.text.delta": "runtime.text.delta",
74
+ "seat.completed": "runtime.turn.completed",
75
+ "seat.failed": "runtime.turn.failed",
76
+ "seat.skipped": "runtime.turn.cancelled",
77
+ }
78
+
79
+ # Which of our kinds are terminal for their scope (turn/session end).
80
+ _TERMINAL_KINDS = frozenset(
81
+ {"board.completed", "seat.completed", "seat.failed", "seat.skipped"}
82
+ )
83
+
84
+
85
+ def _now_iso() -> str:
86
+ """ISO-8601 UTC with an explicit offset (matches the upstream zod
87
+ ``datetime({offset: true})`` contract)."""
88
+ return datetime.now(timezone.utc).isoformat()
89
+
90
+
91
+ def new_session_id() -> str:
92
+ """Mint a per-board-run session id (never reuse the board NAME as the session
93
+ id — that collides across runs and is semantically wrong)."""
94
+ return f"session-{uuid.uuid4().hex[:12]}"
95
+
96
+
97
+ def _turn_id_for(event: AdvisorBoardEvent) -> str | None:
98
+ """A seat projects to a turn; derive a stable turn id from the seat's frozen
99
+ ``seat_key`` label. ``board.*`` events have no turn. Two byte-identical seats
100
+ share a ``seat_key`` (a label, not a unique id, per ABDFREEZE-1) and so map to
101
+ the same turn — an accepted, documented consequence of the label semantics."""
102
+ if not event.seat_key:
103
+ return None
104
+ slug = "".join(c if c.isalnum() else "-" for c in event.seat_key).strip("-")
105
+ return f"turn-{slug or 'seat'}"
106
+
107
+
108
+ def _runtime_payload(event: AdvisorBoardEvent) -> dict[str, Any]:
109
+ """Project our envelope payload onto the runtime.* event's payload shape.
110
+
111
+ Kept faithful to the upstream per-type payloads (events.ts) so a real binding
112
+ validates against ``runtimeEventSchema`` without a second translation."""
113
+ p = dict(event.payload)
114
+ kind = event.kind
115
+ if kind == "board.started":
116
+ return {"state": "idle", "title": str(p.get("title", event.board))}
117
+ if kind == "board.completed":
118
+ return {"reason": "logical_close"}
119
+ if kind == "seat.started":
120
+ return {"message": str(p.get("message", "")), "state": "running"}
121
+ if kind == "seat.text.delta":
122
+ return {"delta": str(p.get("delta", ""))}
123
+ if kind == "seat.completed":
124
+ out = {"outcome": "completed"}
125
+ if p.get("outputSummary"):
126
+ out["outputSummary"] = str(p["outputSummary"])
127
+ return out
128
+ if kind == "seat.failed":
129
+ # Conform to the upstream `runtime_failure.v0.1` (errors.ts): all of
130
+ # schema/category/retryable/actor/scope/message are REQUIRED, so a bare
131
+ # {"reason": ...} would fail zod. A board-seat failure is a non-retryable,
132
+ # harness-scoped, turn-level failure; the message is the leg's detail.
133
+ raw = dict(p.get("failure") or {})
134
+ message = str(raw.get("message") or raw.get("reason") or p.get("status") or "failed") or "failed"
135
+ return {
136
+ "outcome": "failed",
137
+ "failure": {
138
+ "schema": RUNTIME_FAILURE_SCHEMA,
139
+ "category": "internal",
140
+ "retryable": False,
141
+ "actor": "harness",
142
+ "scope": "turn",
143
+ "message": message,
144
+ },
145
+ }
146
+ if kind == "seat.skipped":
147
+ return {"outcome": "cancelled", "reason": str(p.get("reason", "skipped"))}
148
+ return p # pragma: no cover - kinds are exhaustive (EVENT_KINDS)
149
+
150
+
151
+ def map_event_to_runtime_event(
152
+ event: AdvisorBoardEvent, *, session_id: str
153
+ ) -> dict[str, Any]:
154
+ """Map our envelope → an omniagent ``runtime_event.v0.1`` envelope (dict).
155
+
156
+ ``redaction`` is ``metadata_only`` except for the text delta (the advisor's
157
+ own model output, allowed content); we never carry a raw API key (frozen in
158
+ the envelope contract), so nothing here is ``content_redacted``."""
159
+ runtime_type = _KIND_TO_RUNTIME_TYPE.get(event.kind)
160
+ if runtime_type is None: # pragma: no cover - EVENT_KINDS is closed
161
+ raise ValueError(f"no runtime.* mapping for kind {event.kind!r}")
162
+ redaction = "content_allowed" if event.kind == "seat.text.delta" else "metadata_only"
163
+ envelope: dict[str, Any] = {
164
+ "schema": RUNTIME_EVENT_SCHEMA,
165
+ "eventId": f"event-{uuid.uuid4().hex[:12]}",
166
+ "sequence": int(event.sequence),
167
+ "sessionId": session_id,
168
+ "type": runtime_type,
169
+ "occurredAt": event.occurred_at,
170
+ "payload": _runtime_payload(event),
171
+ "redaction": redaction,
172
+ "terminal": event.kind in _TERMINAL_KINDS,
173
+ }
174
+ turn_id = _turn_id_for(event)
175
+ if turn_id is not None:
176
+ envelope["turnId"] = turn_id
177
+ # The board name is our cross-run correlation handle in the plane.
178
+ if event.board:
179
+ envelope["correlationId"] = event.board
180
+ return envelope
181
+
182
+
183
+ def map_event_to_ledger_record(
184
+ event: AdvisorBoardEvent, *, session_id: str, sequence: int | None = None
185
+ ) -> dict[str, Any]:
186
+ """Map our envelope → a ``state_ledger_record.v0.1`` record (dict), kind
187
+ ``runtime_event`` — exactly what ``AuditLedger.appendRuntimeEvent`` writes.
188
+
189
+ ``sequence`` is the ledger's own append cursor (positive, per store); when a
190
+ writer assigns it, pass it through. Defaults to the event's own sequence."""
191
+ runtime_event = map_event_to_runtime_event(event, session_id=session_id)
192
+ record: dict[str, Any] = {
193
+ "schema": LEDGER_RECORD_SCHEMA,
194
+ "recordId": f"record-{uuid.uuid4().hex[:12]}",
195
+ "sequence": int(sequence if sequence is not None else event.sequence),
196
+ "kind": LEDGER_RECORD_KIND,
197
+ "schemaVersion": 1,
198
+ "recordedAt": event.occurred_at,
199
+ "sessionId": session_id,
200
+ "payload": runtime_event,
201
+ }
202
+ if "turnId" in runtime_event:
203
+ record["turnId"] = runtime_event["turnId"]
204
+ return record
205
+
206
+
207
+ # --- cross-language transport seam ------------------------------------------
208
+
209
+
210
+ @runtime_checkable
211
+ class LedgerWriter(Protocol):
212
+ """The documented integration boundary to omniagent-plus's TS state-ledger.
213
+
214
+ A real binding implements ``append_record`` by forwarding the
215
+ ``state_ledger_record.v0.1`` dict to ``AuditLedger.appendRuntimeEvent`` across
216
+ the process/language boundary (IPC / HTTP / a shared append file). This
217
+ module ships :class:`JsonlLedgerWriter` as the reference transport; it is
218
+ deliberately NOT a live write to the real ledger (retention / replay /
219
+ compaction are TS-side)."""
220
+
221
+ def append_record(self, record: Mapping[str, Any]) -> None: ...
222
+
223
+
224
+ class JsonlLedgerWriter:
225
+ """Reference :class:`LedgerWriter`: appends one ``state_ledger_record.v0.1``
226
+ JSON object per line to a file the TS ``AppendOnlyStore`` can ingest/replay.
227
+ A file boundary is the cleanest documented cross-language seam."""
228
+
229
+ def __init__(self, path: Path | str) -> None:
230
+ self._path = Path(path)
231
+ self._lock = threading.Lock()
232
+
233
+ @property
234
+ def path(self) -> Path:
235
+ return self._path
236
+
237
+ def append_record(self, record: Mapping[str, Any]) -> None:
238
+ line = json.dumps(dict(record), separators=(",", ":"), sort_keys=True)
239
+ with self._lock:
240
+ with self._path.open("a", encoding="utf-8") as fh:
241
+ fh.write(line + "\n")
242
+
243
+
244
+ # --- sinks (structurally emit-only: this path can never launch) --------------
245
+
246
+
247
+ class StateLedgerSink:
248
+ """:class:`~events.EventSink` that maps each envelope → a state-ledger record
249
+ and hands it to a :class:`LedgerWriter`. Emit-only by construction — it has
250
+ no ``create_session`` / ``send_turn``, so the observability plane can never
251
+ relaunch a leg (the launcher ≠ observability-plane boundary, in code)."""
252
+
253
+ def __init__(self, writer: LedgerWriter, *, session_id: str | None = None) -> None:
254
+ self._writer = writer
255
+ self._session_id = session_id or new_session_id()
256
+
257
+ @property
258
+ def session_id(self) -> str:
259
+ return self._session_id
260
+
261
+ def emit(self, event: AdvisorBoardEvent) -> None:
262
+ record = map_event_to_ledger_record(event, session_id=self._session_id)
263
+ self._writer.append_record(record)
264
+
265
+
266
+ class CollectingSink:
267
+ """In-memory :class:`~events.EventSink` (tests / a local ui-read-model tail).
268
+ Emit-only, thread-safe."""
269
+
270
+ def __init__(self) -> None:
271
+ self._events: list[AdvisorBoardEvent] = []
272
+ self._lock = threading.Lock()
273
+
274
+ def emit(self, event: AdvisorBoardEvent) -> None:
275
+ with self._lock:
276
+ self._events.append(event)
277
+
278
+ @property
279
+ def events(self) -> tuple[AdvisorBoardEvent, ...]:
280
+ with self._lock:
281
+ return tuple(self._events)
282
+
283
+
284
+ class AsyncForwardingSink:
285
+ """Wrap any :class:`~events.EventSink` and dispatch OFF the leg's critical
286
+ path: ``emit`` does a NON-BLOCKING put and returns immediately; a background
287
+ daemon thread performs the real (possibly slow / failing) downstream write
288
+ via :func:`best_effort_forward`, which swallows every sink error.
289
+
290
+ This is the async half ABDFREEZE-5 deferred: the never-raise guarantee is
291
+ frozen in ``events.best_effort_forward``; the never-DELAY guarantee is here
292
+ (unbounded queue by default; a bounded queue drops on full rather than
293
+ blocking the leg)."""
294
+
295
+ def __init__(self, downstream: EventSink, *, maxsize: int = 0) -> None:
296
+ self._downstream = downstream
297
+ self._queue: queue.Queue[Any] = queue.Queue(maxsize)
298
+ self._thread = threading.Thread(
299
+ target=self._run, name="advisor-board-observability", daemon=True
300
+ )
301
+ self._sentinel = object()
302
+ self._started = False
303
+ self._closed = False
304
+ self._lock = threading.Lock()
305
+ self.dropped = 0 # events shed under back-pressure (bounded queue only)
306
+
307
+ def _ensure_started(self) -> None:
308
+ with self._lock:
309
+ if not self._started and not self._closed:
310
+ self._thread.start()
311
+ self._started = True
312
+
313
+ def emit(self, event: AdvisorBoardEvent) -> None:
314
+ # Non-blocking: a full bounded queue drops the event rather than delaying
315
+ # the leg. Enqueue errors are swallowed — observability never fails a leg.
316
+ self._ensure_started()
317
+ try:
318
+ self._queue.put_nowait(event)
319
+ except queue.Full:
320
+ self.dropped += 1
321
+ except Exception:
322
+ return
323
+
324
+ def _run(self) -> None:
325
+ while True:
326
+ item = self._queue.get()
327
+ try:
328
+ if item is self._sentinel:
329
+ return
330
+ best_effort_forward(self._downstream, item)
331
+ finally:
332
+ self._queue.task_done()
333
+
334
+ def flush(self, timeout: float | None = None) -> bool:
335
+ """Block until the CURRENT backlog has been dispatched. Returns True if it
336
+ drained (best effort; only for a deterministic test / graceful shutdown —
337
+ never called on the leg's critical path)."""
338
+ if not self._started:
339
+ return True
340
+ deadline_join = self._queue.join # join has no timeout; used via close for hard drain
341
+ if timeout is None:
342
+ deadline_join()
343
+ return True
344
+ # Poll unfinished tasks so a hung downstream can't wedge the caller.
345
+ import time
346
+
347
+ end = time.monotonic() + timeout
348
+ while self._queue.unfinished_tasks and time.monotonic() < end:
349
+ time.sleep(0.005)
350
+ return self._queue.unfinished_tasks == 0
351
+
352
+ def close(self, timeout: float | None = 5.0) -> None:
353
+ """Drain deterministically and stop the worker. FIFO guarantees every
354
+ event enqueued before ``close`` is dispatched before the sentinel, so a
355
+ test can assert the plane's contents right after ``close``."""
356
+ with self._lock:
357
+ if self._closed:
358
+ return
359
+ self._closed = True
360
+ if not self._started:
361
+ return
362
+ self._queue.put(self._sentinel)
363
+ self._thread.join(timeout)
364
+
365
+
366
+ # --- the board observer (swallow-all emit around a board run) ----------------
367
+
368
+
369
+ def _status_to_seat_kind(status: str) -> str:
370
+ """Map a ``PanelLegResult.status`` to the terminal seat event kind."""
371
+ s = (status or "").upper()
372
+ if s in {"OK", "EMPTY"}:
373
+ return "seat.completed"
374
+ if s == "UNAVAILABLE":
375
+ return "seat.skipped"
376
+ return "seat.failed" # DEGRADED / ERROR / TIMEOUT
377
+
378
+
379
+ class BoardObserver:
380
+ """Emit the ``board.*`` / ``seat.*`` envelope sequence for one board run.
381
+
382
+ EVERY emit is swallow-all: the AdvisorBoardEvent constructor can raise on a
383
+ bad kind, the mapping can raise, and the enqueue can raise — none of that may
384
+ delay or fail the native leg, so the whole construct+map+forward path is
385
+ wrapped. A ``None`` sink is a cheap no-op (keeps the default board
386
+ byte-neutral: no envelope is even built)."""
387
+
388
+ def __init__(self, sink: EventSink | None, *, board_name: str) -> None:
389
+ self._sink = sink
390
+ self._board = board_name
391
+ self._seq = 0
392
+ self._lock = threading.Lock()
393
+
394
+ @property
395
+ def enabled(self) -> bool:
396
+ return self._sink is not None
397
+
398
+ def _emit(
399
+ self,
400
+ kind: str,
401
+ *,
402
+ seat_key: str = "",
403
+ vendor_family: str = "",
404
+ harness: str = "",
405
+ payload: Mapping[str, Any] | None = None,
406
+ ) -> None:
407
+ if self._sink is None:
408
+ return
409
+ try:
410
+ with self._lock:
411
+ self._seq += 1
412
+ seq = self._seq
413
+ event = AdvisorBoardEvent(
414
+ kind=kind,
415
+ board=self._board,
416
+ sequence=seq,
417
+ occurred_at=_now_iso(),
418
+ seat_key=seat_key,
419
+ vendor_family=vendor_family,
420
+ harness=harness,
421
+ payload=dict(payload or {}),
422
+ )
423
+ best_effort_forward(self._sink, event)
424
+ except Exception:
425
+ # Observability NEVER touches the leg — swallow construction/map/enqueue.
426
+ return
427
+
428
+ def board_started(self) -> None:
429
+ self._emit("board.started", payload={"title": self._board})
430
+
431
+ def board_completed(self, results: Any) -> None:
432
+ try:
433
+ legs = list(results)
434
+ ok = sum(1 for r in legs if getattr(r, "status", "") in {"OK", "EMPTY"})
435
+ payload = {"seats": len(legs), "ok": ok}
436
+ except Exception:
437
+ payload = {}
438
+ self._emit("board.completed", payload=payload)
439
+
440
+ def _seat_fields(self, seat: Any) -> dict[str, str]:
441
+ try:
442
+ return {
443
+ "seat_key": str(getattr(seat, "seat_key", "") or ""),
444
+ "vendor_family": str(getattr(seat, "vendor_family", "") or ""),
445
+ "harness": str(getattr(seat, "harness", "") or ""),
446
+ }
447
+ except Exception:
448
+ return {"seat_key": "", "vendor_family": "", "harness": ""}
449
+
450
+ def seat_started(self, seat: Any) -> None:
451
+ self._emit("seat.started", **self._seat_fields(seat))
452
+
453
+ def seat_result(self, seat: Any, result: Any) -> None:
454
+ fields = self._seat_fields(seat)
455
+ status = str(getattr(result, "status", "") or "")
456
+ kind = _status_to_seat_kind(status)
457
+ payload: dict[str, Any] = {"status": status}
458
+ detail = getattr(result, "detail", None)
459
+ if kind == "seat.skipped" and detail:
460
+ payload["reason"] = str(detail)
461
+ if kind == "seat.failed":
462
+ payload["failure"] = {"reason": str(detail or status)}
463
+ self._emit(kind, payload=payload, **fields)
464
+
465
+
466
+ __all__ = [
467
+ "WORKLOAD_BOARD",
468
+ "WORKLOAD_PHASE_EXECUTION",
469
+ "LEDGER_RECORD_SCHEMA",
470
+ "RUNTIME_EVENT_SCHEMA",
471
+ "LEDGER_RECORD_KIND",
472
+ "new_session_id",
473
+ "map_event_to_runtime_event",
474
+ "map_event_to_ledger_record",
475
+ "LedgerWriter",
476
+ "JsonlLedgerWriter",
477
+ "StateLedgerSink",
478
+ "CollectingSink",
479
+ "AsyncForwardingSink",
480
+ "BoardObserver",
481
+ ]