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,625 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ import shutil
6
+ import subprocess
7
+ from dataclasses import asdict, dataclass
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Any, Callable
11
+
12
+
13
+ AGENT_VIEW_STATES = {"running", "done", "blocked", "stopped", "failed", "unknown"}
14
+ SECRET_LIKE_KEYS = {
15
+ "api_key",
16
+ "authorization",
17
+ "content",
18
+ "data",
19
+ "env",
20
+ "environment",
21
+ "key",
22
+ "log",
23
+ "logs",
24
+ "payload",
25
+ "private_key",
26
+ "provider_payload",
27
+ "raw",
28
+ "secret",
29
+ "stderr",
30
+ "stdout",
31
+ "text",
32
+ "token",
33
+ }
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class BlockerSummary:
38
+ reason: str
39
+ summary: str
40
+
41
+ def to_json(self) -> dict[str, str]:
42
+ return asdict(self)
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class AgentViewSession:
47
+ id: str | None
48
+ session_id: str | None
49
+ cwd: str | None
50
+ kind: str | None
51
+ state: str
52
+ status: str | None
53
+ name: str | None
54
+ started_at: str | None
55
+ completed_at: str | None
56
+ pid: int | None
57
+ metadata: dict[str, Any]
58
+
59
+ def to_json(self) -> dict[str, Any]:
60
+ return asdict(self)
61
+
62
+
63
+ @dataclass(frozen=True)
64
+ class CommandResult:
65
+ command: tuple[str, ...]
66
+ returncode: int | None
67
+ output: str = ""
68
+ blocker: BlockerSummary | None = None
69
+
70
+ @property
71
+ def ok(self) -> bool:
72
+ return self.returncode == 0 and self.blocker is None
73
+
74
+ def to_json(self) -> dict[str, Any]:
75
+ return {
76
+ "command": list(self.command),
77
+ "returncode": self.returncode,
78
+ "output": self.output,
79
+ "blocker": self.blocker.to_json() if self.blocker else None,
80
+ }
81
+
82
+
83
+ @dataclass(frozen=True)
84
+ class AgentViewLifecycleResult:
85
+ session_id: str
86
+ state: str
87
+ cwd: str | None
88
+ logs_ref: str | None
89
+ started_at: str | None
90
+ completed_at: str | None
91
+ stop_result: str | None
92
+ auth_posture: str = "unknown"
93
+ billing_posture: str = "unknown"
94
+ blocker: BlockerSummary | None = None
95
+
96
+ def to_json(self) -> dict[str, Any]:
97
+ return {
98
+ "session_id": self.session_id,
99
+ "state": self.state,
100
+ "cwd": self.cwd,
101
+ "logs_ref": self.logs_ref,
102
+ "started_at": self.started_at,
103
+ "completed_at": self.completed_at,
104
+ "stop_result": self.stop_result,
105
+ "auth_posture": self.auth_posture,
106
+ "billing_posture": self.billing_posture,
107
+ }
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class AgentViewListResult:
112
+ command: tuple[str, ...]
113
+ sessions: tuple[AgentViewSession, ...]
114
+ returncode: int | None
115
+ blocker: BlockerSummary | None = None
116
+
117
+ @property
118
+ def ok(self) -> bool:
119
+ return self.returncode == 0 and self.blocker is None
120
+
121
+ def to_json(self) -> dict[str, Any]:
122
+ return {
123
+ "command": list(self.command),
124
+ "sessions": [session.to_json() for session in self.sessions],
125
+ "returncode": self.returncode,
126
+ "blocker": self.blocker.to_json() if self.blocker else None,
127
+ }
128
+
129
+
130
+ @dataclass(frozen=True)
131
+ class LaunchPreflightResult:
132
+ trusted: bool
133
+ trust_state: dict[str, str]
134
+ command: tuple[str, ...]
135
+ blocker: BlockerSummary | None = None
136
+
137
+ def to_json(self) -> dict[str, Any]:
138
+ return {
139
+ "trusted": self.trusted,
140
+ "trust_state": dict(self.trust_state),
141
+ "command": list(self.command),
142
+ "blocker": self.blocker.to_json() if self.blocker else None,
143
+ }
144
+
145
+
146
+ class ClaudeAgentViewAdapter:
147
+ def __init__(
148
+ self,
149
+ *,
150
+ claude_bin: str = "claude",
151
+ runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run,
152
+ ) -> None:
153
+ self.claude_bin = claude_bin
154
+ self._runner = runner
155
+
156
+ def list_command(self) -> list[str]:
157
+ return [self.claude_bin, "agents", "--json", "--all"]
158
+
159
+ def launch_command(
160
+ self,
161
+ prompt: str | None,
162
+ *,
163
+ cwd: str | Path | None = None,
164
+ model: str | None = None,
165
+ effort: str | None = None,
166
+ permission: str | None = None,
167
+ name: str | None = None,
168
+ plugin_dirs: list[str | Path] | None = None,
169
+ settings: str | Path | None = None,
170
+ mcp_config: str | Path | None = None,
171
+ add_dirs: list[str | Path] | None = None,
172
+ safe_mode: bool = False,
173
+ strict_mcp_config: bool = False,
174
+ tools: str | list[str] | None = None,
175
+ ) -> list[str]:
176
+ command = [self.claude_bin, "--bg"]
177
+ if safe_mode:
178
+ command.append("--safe-mode")
179
+ if name:
180
+ command.extend(["--name", name])
181
+ if cwd is not None:
182
+ command.extend(["--cwd", str(cwd)])
183
+ if model:
184
+ command.extend(["--model", model])
185
+ if effort:
186
+ command.extend(["--effort", effort])
187
+ if permission:
188
+ command.extend(["--permission-mode", permission])
189
+ for plugin_dir in plugin_dirs or []:
190
+ command.extend(["--plugin-dir", str(plugin_dir)])
191
+ if settings is not None:
192
+ command.extend(["--settings", str(settings)])
193
+ if strict_mcp_config:
194
+ command.append("--strict-mcp-config")
195
+ if mcp_config is not None:
196
+ command.extend(["--mcp-config", str(mcp_config)])
197
+ for add_dir in add_dirs or []:
198
+ command.extend(["--add-dir", str(add_dir)])
199
+ if tools is not None:
200
+ command.append("--tools")
201
+ if isinstance(tools, str):
202
+ command.append(tools)
203
+ else:
204
+ command.extend(tools)
205
+ if prompt is not None:
206
+ command.append(prompt)
207
+ return command
208
+
209
+ def logs_command(self, agent_id: str) -> list[str]:
210
+ return [self.claude_bin, "logs", _validated_agent_id(agent_id)]
211
+
212
+ def attach_command(self, agent_id: str) -> list[str]:
213
+ return [self.claude_bin, "attach", _validated_agent_id(agent_id)]
214
+
215
+ def stop_command(self, agent_id: str) -> list[str]:
216
+ return [self.claude_bin, "stop", _validated_agent_id(agent_id)]
217
+
218
+ def remove_command(self, agent_id: str) -> list[str]:
219
+ return [self.claude_bin, "rm", _validated_agent_id(agent_id)]
220
+
221
+ def list_sessions(self, *, cwd: str | Path | None = None) -> AgentViewListResult:
222
+ command = self.list_command()
223
+ result = self._runner(
224
+ command,
225
+ cwd=str(cwd) if cwd is not None else None,
226
+ text=True,
227
+ stdout=subprocess.PIPE,
228
+ stderr=subprocess.STDOUT,
229
+ check=False,
230
+ )
231
+ if result.returncode != 0:
232
+ return AgentViewListResult(
233
+ command=tuple(command),
234
+ sessions=(),
235
+ returncode=result.returncode,
236
+ blocker=BlockerSummary("agents_list_failed", "claude agents did not return a successful session list."),
237
+ )
238
+ try:
239
+ payload = json.loads(result.stdout)
240
+ except json.JSONDecodeError:
241
+ return AgentViewListResult(
242
+ command=tuple(command),
243
+ sessions=(),
244
+ returncode=result.returncode,
245
+ blocker=BlockerSummary("agents_list_non_json", "claude agents returned non-JSON output."),
246
+ )
247
+ records = payload.get("agents") if isinstance(payload, dict) else payload
248
+ if not isinstance(records, list):
249
+ return AgentViewListResult(
250
+ command=tuple(command),
251
+ sessions=(),
252
+ returncode=result.returncode,
253
+ blocker=BlockerSummary("agents_list_unexpected_shape", "claude agents JSON did not contain a session array."),
254
+ )
255
+ sessions = tuple(_session_from_payload(record) for record in records if isinstance(record, dict))
256
+ return AgentViewListResult(command=tuple(command), sessions=sessions, returncode=result.returncode)
257
+
258
+ def prepare_launch(self, prompt: str, *, cwd: str | Path, **kwargs: Any) -> LaunchPreflightResult:
259
+ command = tuple(self.launch_command(prompt, cwd=cwd, **kwargs))
260
+ trust_state = workspace_trust_state(Path(cwd))
261
+ if trust_state["status"] != "trusted":
262
+ return LaunchPreflightResult(
263
+ trusted=False,
264
+ trust_state=trust_state,
265
+ command=command,
266
+ blocker=BlockerSummary("trust_preflight_blocked", "Agent View launch blocked before claude --bg because workspace or MCP trust is not ready."),
267
+ )
268
+ if shutil.which(self.claude_bin) is None:
269
+ return LaunchPreflightResult(
270
+ trusted=False,
271
+ trust_state=trust_state,
272
+ command=command,
273
+ blocker=BlockerSummary("missing_claude_cli", "Agent View launch blocked because the Claude CLI is not available."),
274
+ )
275
+ support = self._runner(
276
+ [self.claude_bin, "--bg", "--help"],
277
+ text=True,
278
+ stdout=subprocess.PIPE,
279
+ stderr=subprocess.STDOUT,
280
+ check=False,
281
+ )
282
+ if support.returncode != 0:
283
+ return LaunchPreflightResult(
284
+ trusted=False,
285
+ trust_state=trust_state,
286
+ command=command,
287
+ blocker=BlockerSummary("unsupported_launch", "Agent View launch blocked because claude --bg is not supported by this Claude CLI."),
288
+ )
289
+ return LaunchPreflightResult(trusted=True, trust_state=trust_state, command=command)
290
+
291
+ def launch_background(self, prompt: str, *, cwd: str | Path, **kwargs: Any) -> AgentViewLifecycleResult:
292
+ preflight = self.prepare_launch(prompt, cwd=cwd, **kwargs)
293
+ if not preflight.trusted:
294
+ return AgentViewLifecycleResult(
295
+ session_id="preflight",
296
+ state="blocked",
297
+ cwd=str(cwd),
298
+ logs_ref=None,
299
+ started_at=None,
300
+ completed_at=_utc_now(),
301
+ stop_result=None,
302
+ blocker=preflight.blocker,
303
+ )
304
+ result = self._runner(
305
+ list(preflight.command),
306
+ cwd=str(cwd),
307
+ text=True,
308
+ stdout=subprocess.PIPE,
309
+ stderr=subprocess.STDOUT,
310
+ check=False,
311
+ )
312
+ if result.returncode != 0:
313
+ return AgentViewLifecycleResult(
314
+ session_id="launch",
315
+ state="blocked",
316
+ cwd=str(cwd),
317
+ logs_ref=None,
318
+ started_at=None,
319
+ completed_at=_utc_now(),
320
+ stop_result=None,
321
+ blocker=BlockerSummary("agent_view_launch_failed", "claude --bg did not launch a background session."),
322
+ )
323
+
324
+ session_id = _launch_session_id(result.stdout)
325
+ session = _find_session(self.list_sessions(cwd=cwd).sessions, session_id=session_id, cwd=str(cwd))
326
+ if session:
327
+ return _lifecycle_from_session(session)
328
+ return _lifecycle_from_parts(
329
+ session_id=session_id or "unknown",
330
+ state="running",
331
+ cwd=str(cwd),
332
+ started_at=_utc_now(),
333
+ completed_at=None,
334
+ stop_result=None,
335
+ )
336
+
337
+ def inspect(self, agent_id: str, *, cwd: str | Path | None = None) -> AgentViewLifecycleResult:
338
+ agent_id = _validated_agent_id(agent_id)
339
+ listed = self.list_sessions(cwd=cwd)
340
+ if not listed.ok:
341
+ return _lifecycle_from_parts(
342
+ session_id=agent_id,
343
+ state="blocked",
344
+ cwd=str(cwd) if cwd is not None else None,
345
+ started_at=None,
346
+ completed_at=_utc_now(),
347
+ stop_result=None,
348
+ blocker=listed.blocker,
349
+ )
350
+ session = _find_session(listed.sessions, session_id=agent_id, cwd=str(cwd) if cwd is not None else None)
351
+ if session:
352
+ return _lifecycle_from_session(session)
353
+ return _lifecycle_from_parts(
354
+ session_id=agent_id,
355
+ state="stale",
356
+ cwd=str(cwd) if cwd is not None else None,
357
+ started_at=None,
358
+ completed_at=None,
359
+ stop_result=None,
360
+ blocker=BlockerSummary("agent_view_session_missing", "claude agents did not list the requested background session."),
361
+ )
362
+
363
+ def logs(self, agent_id: str, *, cwd: str | Path | None = None) -> CommandResult:
364
+ command = self.logs_command(agent_id)
365
+ result = self._runner(
366
+ command,
367
+ cwd=str(cwd) if cwd is not None else None,
368
+ text=True,
369
+ stdout=subprocess.PIPE,
370
+ stderr=subprocess.STDOUT,
371
+ check=False,
372
+ )
373
+ blocker = None
374
+ if result.returncode != 0:
375
+ blocker = BlockerSummary("logs_command_failed", "claude logs did not return human-readable session text.")
376
+ return CommandResult(command=tuple(command), returncode=result.returncode, output=result.stdout, blocker=blocker)
377
+
378
+ def attach(self, agent_id: str, *, cwd: str | Path | None = None) -> CommandResult:
379
+ command = self.attach_command(agent_id)
380
+ result = self._runner(
381
+ command,
382
+ cwd=str(cwd) if cwd is not None else None,
383
+ text=True,
384
+ stdout=subprocess.PIPE,
385
+ stderr=subprocess.STDOUT,
386
+ check=False,
387
+ )
388
+ blocker = None
389
+ if result.returncode != 0:
390
+ blocker = BlockerSummary("attach_command_failed", "claude attach did not attach to the background session.")
391
+ return CommandResult(command=tuple(command), returncode=result.returncode, output="", blocker=blocker)
392
+
393
+ def stop(self, agent_id: str, *, cwd: str | Path | None = None) -> AgentViewLifecycleResult:
394
+ command = self.stop_command(agent_id)
395
+ result = self._runner(
396
+ command,
397
+ cwd=str(cwd) if cwd is not None else None,
398
+ text=True,
399
+ stdout=subprocess.PIPE,
400
+ stderr=subprocess.STDOUT,
401
+ check=False,
402
+ )
403
+ if result.returncode != 0:
404
+ return _lifecycle_from_parts(
405
+ session_id=_validated_agent_id(agent_id),
406
+ state="blocked",
407
+ cwd=str(cwd) if cwd is not None else None,
408
+ started_at=None,
409
+ completed_at=_utc_now(),
410
+ stop_result="failed",
411
+ blocker=BlockerSummary("stop_command_failed", "claude stop did not stop the background session."),
412
+ )
413
+ inspected = self.inspect(agent_id, cwd=cwd)
414
+ return _lifecycle_from_parts(
415
+ session_id=inspected.session_id,
416
+ state="stopped" if inspected.state in {"stale", "unknown"} else inspected.state,
417
+ cwd=inspected.cwd,
418
+ started_at=inspected.started_at,
419
+ completed_at=inspected.completed_at or _utc_now(),
420
+ stop_result="stopped",
421
+ auth_posture=inspected.auth_posture,
422
+ billing_posture=inspected.billing_posture,
423
+ )
424
+
425
+ def remove(self, agent_id: str, *, cwd: str | Path | None = None) -> CommandResult:
426
+ command = self.remove_command(agent_id)
427
+ result = self._runner(
428
+ command,
429
+ cwd=str(cwd) if cwd is not None else None,
430
+ text=True,
431
+ stdout=subprocess.PIPE,
432
+ stderr=subprocess.STDOUT,
433
+ check=False,
434
+ )
435
+ blocker = None
436
+ if result.returncode != 0:
437
+ blocker = BlockerSummary("remove_refused", "claude rm refused to remove the session record.")
438
+ return CommandResult(command=tuple(command), returncode=result.returncode, output="", blocker=blocker)
439
+
440
+
441
+ def workspace_trust_state(cwd: Path) -> dict[str, str]:
442
+ mcp_path = cwd / ".mcp.json"
443
+ mcp_status = "absent"
444
+ if mcp_path.exists():
445
+ try:
446
+ payload = json.loads(mcp_path.read_text(encoding="utf-8"))
447
+ except json.JSONDecodeError:
448
+ return {"status": "blocked", "workspace": "trusted", "mcp": "invalid_json"}
449
+ rendered = json.dumps(payload, sort_keys=True)
450
+ if "pmcp" in rendered and "pending" in rendered.lower():
451
+ return {"status": "blocked", "workspace": "trusted", "mcp": "pmcp_pending_approval"}
452
+ mcp_status = "present"
453
+ return {"status": "trusted", "workspace": "trusted", "mcp": mcp_status}
454
+
455
+
456
+ def _session_from_payload(payload: dict[str, Any]) -> AgentViewSession:
457
+ state = _agent_state(payload.get("state") or payload.get("status"))
458
+ return AgentViewSession(
459
+ id=_optional_str(payload.get("id") or payload.get("agent_id")),
460
+ session_id=_optional_str(payload.get("session_id") or payload.get("sessionId")),
461
+ cwd=_optional_str(payload.get("cwd") or payload.get("workspace")),
462
+ kind=_optional_str(payload.get("kind") or payload.get("type")),
463
+ state=state,
464
+ status=_optional_str(payload.get("status")),
465
+ name=_optional_str(payload.get("name")),
466
+ started_at=_optional_str(payload.get("started_at") or payload.get("startedAt")),
467
+ completed_at=_optional_str(payload.get("completed_at") or payload.get("completedAt") or payload.get("finished_at") or payload.get("finishedAt")),
468
+ pid=_optional_int(payload.get("pid")),
469
+ metadata=_metadata_only(payload),
470
+ )
471
+
472
+
473
+ def _agent_state(value: Any) -> str:
474
+ normalized = str(value or "").strip().lower().replace("-", "_")
475
+ if normalized in {"running", "started", "starting", "active", "working"}:
476
+ return "running"
477
+ if normalized in {"done", "complete", "completed", "success", "succeeded", "finished"}:
478
+ return "done"
479
+ if normalized in {"blocked", "waiting", "needs_input", "permission_required"}:
480
+ return "blocked"
481
+ if normalized in {"stopped", "cancelled", "canceled", "terminated", "killed"}:
482
+ return "stopped"
483
+ if normalized in {"failed", "failure", "error", "errored", "crashed"}:
484
+ return "failed"
485
+ return "unknown"
486
+
487
+
488
+ def _metadata_only(value: Any) -> Any:
489
+ if isinstance(value, dict):
490
+ clean = {}
491
+ for key, item in value.items():
492
+ if _secret_like_key(key):
493
+ continue
494
+ clean[str(key)] = _metadata_only(item)
495
+ return clean
496
+ if isinstance(value, list):
497
+ return [_metadata_only(item) for item in value if isinstance(item, (dict, list, str, int, float, bool)) or item is None]
498
+ if isinstance(value, (str, int, float, bool)) or value is None:
499
+ return value
500
+ return str(type(value).__name__)
501
+
502
+
503
+ def _secret_like_key(key: object) -> bool:
504
+ normalized = str(key).lower().replace("-", "_")
505
+ return any(part in SECRET_LIKE_KEYS for part in re.split(r"[^a-z0-9_]+", normalized)) or normalized in SECRET_LIKE_KEYS
506
+
507
+
508
+ def _validated_agent_id(agent_id: str) -> str:
509
+ if not isinstance(agent_id, str) or not re.fullmatch(r"[A-Za-z0-9._:-]+", agent_id):
510
+ raise ValueError("agent id must contain only stable CLI identifier characters")
511
+ return agent_id
512
+
513
+
514
+ def _lifecycle_from_session(session: AgentViewSession) -> AgentViewLifecycleResult:
515
+ session_id = session.session_id or session.id or "unknown"
516
+ failed = session.state == "failed"
517
+ return _lifecycle_from_parts(
518
+ session_id=session_id,
519
+ state=session.state,
520
+ cwd=session.cwd,
521
+ started_at=session.started_at,
522
+ completed_at=(
523
+ (session.completed_at or _utc_now())
524
+ if failed
525
+ else (session.completed_at if session.state in {"done", "blocked", "stopped"} else None)
526
+ ),
527
+ stop_result="stopped" if session.state == "stopped" else None,
528
+ auth_posture=_agent_auth_posture(session.metadata),
529
+ billing_posture=_agent_billing_posture(session.metadata),
530
+ blocker=BlockerSummary("agent_view_failed", "Claude Agent View reported a terminal failed state.") if failed else None,
531
+ )
532
+
533
+
534
+ def _lifecycle_from_parts(
535
+ *,
536
+ session_id: str,
537
+ state: str,
538
+ cwd: str | None,
539
+ started_at: str | None,
540
+ completed_at: str | None,
541
+ stop_result: str | None,
542
+ auth_posture: str = "unknown",
543
+ billing_posture: str = "unknown",
544
+ blocker: BlockerSummary | None = None,
545
+ ) -> AgentViewLifecycleResult:
546
+ session_id = session_id if session_id and session_id != "unknown" else "unknown"
547
+ state = state if state in AGENT_VIEW_STATES else "unknown"
548
+ return AgentViewLifecycleResult(
549
+ session_id=session_id,
550
+ state=state,
551
+ cwd=cwd,
552
+ logs_ref=f"claude logs {session_id}" if session_id != "unknown" else None,
553
+ started_at=started_at,
554
+ completed_at=completed_at,
555
+ stop_result=stop_result,
556
+ auth_posture=auth_posture,
557
+ billing_posture=billing_posture,
558
+ blocker=blocker,
559
+ )
560
+
561
+
562
+ def _find_session(sessions: tuple[AgentViewSession, ...], *, session_id: str | None, cwd: str | None) -> AgentViewSession | None:
563
+ if session_id:
564
+ for session in sessions:
565
+ if session.id == session_id or session.session_id == session_id:
566
+ return session
567
+ if cwd:
568
+ for session in sessions:
569
+ if session.cwd == cwd and session.state in {"running", "blocked"}:
570
+ return session
571
+ return sessions[0] if len(sessions) == 1 else None
572
+
573
+
574
+ def _launch_session_id(output: str) -> str | None:
575
+ text = str(output or "").strip()
576
+ if not text:
577
+ return None
578
+ try:
579
+ payload = json.loads(text)
580
+ except json.JSONDecodeError:
581
+ payload = None
582
+ if isinstance(payload, dict):
583
+ value = payload.get("id") or payload.get("agent_id") or payload.get("agentId") or payload.get("session_id") or payload.get("sessionId")
584
+ if isinstance(value, str) and re.fullmatch(r"[A-Za-z0-9._:-]+", value):
585
+ return value
586
+ for pattern in (
587
+ r"\b(?:agent|agent_id|session|session_id)\s*[:=]\s*([A-Za-z0-9._:-]+)",
588
+ r"\b([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\b",
589
+ ):
590
+ match = re.search(pattern, text, flags=re.IGNORECASE)
591
+ if match:
592
+ return match.group(1)
593
+ return None
594
+
595
+
596
+ def _agent_auth_posture(metadata: dict[str, Any]) -> str:
597
+ auth = metadata.get("auth_posture") if isinstance(metadata.get("auth_posture"), dict) else metadata
598
+ method = str(auth.get("method") or auth.get("authMethod") or auth.get("provider") or "").lower()
599
+ status = str(auth.get("status") or "").lower()
600
+ if method == "subscription" and status in {"authenticated", "ok", "ready"}:
601
+ return "subscription_local"
602
+ if method in {"api_key", "apikey", "key"}:
603
+ return "api_key"
604
+ return "unknown"
605
+
606
+
607
+ def _agent_billing_posture(metadata: dict[str, Any]) -> str:
608
+ auth = _agent_auth_posture(metadata)
609
+ if auth == "subscription_local":
610
+ return "subscription_included"
611
+ if auth == "api_key":
612
+ return "api_key_billed"
613
+ return "unknown"
614
+
615
+
616
+ def _utc_now() -> str:
617
+ return datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
618
+
619
+
620
+ def _optional_str(value: Any) -> str | None:
621
+ return value if isinstance(value, str) else None
622
+
623
+
624
+ def _optional_int(value: Any) -> int | None:
625
+ return value if isinstance(value, int) and not isinstance(value, bool) else None