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.
- phase_loop_runtime/__init__.py +20 -0
- phase_loop_runtime/_contract_docs/phase-loop/extraction-readiness.md +90 -0
- phase_loop_runtime/_contract_docs/phase-loop/granular-execution-policy.md +137 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-capability-matrix.md +510 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-skill-matrix.md +125 -0
- phase_loop_runtime/_contract_docs/phase-loop/harness-substrate-manifest.md +232 -0
- phase_loop_runtime/_contract_docs/phase-loop/pi-loop-control.md +247 -0
- phase_loop_runtime/_contract_docs/phase-loop/protocol.md +2759 -0
- phase_loop_runtime/_contract_docs/phase-loop/runtime-boundary.md +289 -0
- phase_loop_runtime/_contract_docs/phase-loop/spec-discovery-roots.md +47 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-governed-pipeline.md +42 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-portal-projection.md +34 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-regenesis.md +29 -0
- phase_loop_runtime/_contract_docs/phase-loop/substrate-soak-report.md +32 -0
- phase_loop_runtime/_contract_docs/runtime/verification-evidence-contract.md +52 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/commands.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-mixed-signals/mixed.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-provider-timeout/timeout-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-redacted-summary/redacted-summary.json +6 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-small-sample/sample.json +7 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/edge.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/borderline/borderline-threshold-edge/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-a.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-b.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-duplicate-png/shot-c.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/claimed-results.json +5 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-missing-references/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-subtle-low-variance/metrics.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/findings.md +10 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-templated-prose/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/scores.json +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-fake/fake-uniform-scores/summary.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/bundle-check.json +8 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-adoption-bundle/operator-note.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/events-sample.jsonl +3 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-handoff-ledger/receipt.txt +1 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/evidence.json +13 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-runner-diff/summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/matrix.json +9 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-test-matrix/notes.md +4 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/console-summary.txt +2 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/manifest.json +16 -0
- phase_loop_runtime/_test_fixtures/evidence-audit-calibration/known-real/real-ui-screenshot-standin/screenshot-standin.ppm +5 -0
- phase_loop_runtime/adoption_bundle.py +273 -0
- phase_loop_runtime/advisor_board/CONTRACTS.md +257 -0
- phase_loop_runtime/advisor_board/__init__.py +288 -0
- phase_loop_runtime/advisor_board/backing.py +137 -0
- phase_loop_runtime/advisor_board/backing_omnigent.py +498 -0
- phase_loop_runtime/advisor_board/config.py +225 -0
- phase_loop_runtime/advisor_board/events.py +115 -0
- phase_loop_runtime/advisor_board/fixtures/advisor-boards.example.toml +59 -0
- phase_loop_runtime/advisor_board/fixtures.py +91 -0
- phase_loop_runtime/advisor_board/harness_mapping.py +125 -0
- phase_loop_runtime/advisor_board/matrix.py +146 -0
- phase_loop_runtime/advisor_board/observability.py +481 -0
- phase_loop_runtime/advisor_board/presets.py +188 -0
- phase_loop_runtime/advisor_board/registries.py +309 -0
- phase_loop_runtime/advisor_board/resolver.py +253 -0
- phase_loop_runtime/advisor_board/schema.py +269 -0
- phase_loop_runtime/advisor_board/standin.py +166 -0
- phase_loop_runtime/advisor_board/validation.py +159 -0
- phase_loop_runtime/agent_runtime_provider.py +676 -0
- phase_loop_runtime/baml_modular.py +656 -0
- phase_loop_runtime/baml_src/dotfiles_adoption_manifest.baml +37 -0
- phase_loop_runtime/baml_src/dotfiles_c4_document.baml +14 -0
- phase_loop_runtime/baml_src/dotfiles_plan_manifest.baml +50 -0
- phase_loop_runtime/baml_src/dotfiles_runtime_projection.baml +16 -0
- phase_loop_runtime/baml_src/dotfiles_task_catalog.baml +20 -0
- phase_loop_runtime/baml_src/emit_phase_closeout.baml +86 -0
- phase_loop_runtime/baml_src/evaluate_suspected_fake_evidence.baml +36 -0
- phase_loop_runtime/baml_src/verification_evidence.baml +40 -0
- phase_loop_runtime/broker.py +181 -0
- phase_loop_runtime/build_bundle.py +294 -0
- phase_loop_runtime/capability_registry.py +835 -0
- phase_loop_runtime/classifier.py +87 -0
- phase_loop_runtime/claude_agent_view.py +625 -0
- phase_loop_runtime/claude_channel_sidecar.py +1086 -0
- phase_loop_runtime/cli.py +2599 -0
- phase_loop_runtime/closeout.py +865 -0
- phase_loop_runtime/closeout_classifier.py +120 -0
- phase_loop_runtime/closeout_evidence_audit.py +112 -0
- phase_loop_runtime/closeout_validation.py +144 -0
- phase_loop_runtime/closeout_validators.py +203 -0
- phase_loop_runtime/consiliency_gates.py +374 -0
- phase_loop_runtime/consiliency_ingest.py +399 -0
- phase_loop_runtime/consiliency_layout.py +176 -0
- phase_loop_runtime/consiliency_scaffold.py +305 -0
- phase_loop_runtime/cross_repo_channel.py +331 -0
- phase_loop_runtime/discovery.py +2368 -0
- phase_loop_runtime/dispatch_lock.py +167 -0
- phase_loop_runtime/doc_delta_validator.py +98 -0
- phase_loop_runtime/docs_audit.py +285 -0
- phase_loop_runtime/docs_freshness.py +468 -0
- phase_loop_runtime/docs_surfaces.py +150 -0
- phase_loop_runtime/dotfiles_profile_plugin.py +96 -0
- phase_loop_runtime/events.py +154 -0
- phase_loop_runtime/events_migration.py +96 -0
- phase_loop_runtime/evidence_audit.py +1082 -0
- phase_loop_runtime/evidence_audit_config.py +172 -0
- phase_loop_runtime/fleet_map.py +426 -0
- phase_loop_runtime/fleet_metrics.py +298 -0
- phase_loop_runtime/fleet_metrics_export.py +196 -0
- phase_loop_runtime/git_discipline.py +479 -0
- phase_loop_runtime/git_ops.py +122 -0
- phase_loop_runtime/git_topology.py +252 -0
- phase_loop_runtime/governed_bundle.py +137 -0
- phase_loop_runtime/governed_premerge.py +219 -0
- phase_loop_runtime/governed_review.py +264 -0
- phase_loop_runtime/handoff.py +932 -0
- phase_loop_runtime/handoff_path.py +14 -0
- phase_loop_runtime/injection.py +740 -0
- phase_loop_runtime/install_status.py +88 -0
- phase_loop_runtime/lane_scheduler.py +317 -0
- phase_loop_runtime/launcher.py +2760 -0
- phase_loop_runtime/lease_store.py +553 -0
- phase_loop_runtime/maintenance.py +401 -0
- phase_loop_runtime/migrate_handoffs.py +201 -0
- phase_loop_runtime/models.py +2144 -0
- phase_loop_runtime/observability.py +1290 -0
- phase_loop_runtime/panel_invoker.py +1638 -0
- phase_loop_runtime/phase_loop_drift_audit.py +388 -0
- phase_loop_runtime/phase_worktree_executor.py +433 -0
- phase_loop_runtime/pipeline_adapter/__init__.py +12 -0
- phase_loop_runtime/pipeline_adapter/branch_ops.py +274 -0
- phase_loop_runtime/pipeline_adapter/flag.py +55 -0
- phase_loop_runtime/pipeline_adapter/markers.py +13 -0
- phase_loop_runtime/pipeline_adapter/merge_policy.py +72 -0
- phase_loop_runtime/pipeline_adapter/ratification.py +100 -0
- phase_loop_runtime/pipeline_adapter/sibling_matcher.py +123 -0
- phase_loop_runtime/plan_ir.py +523 -0
- phase_loop_runtime/plan_manifest.py +415 -0
- phase_loop_runtime/planner_validation.py +176 -0
- phase_loop_runtime/profiles.py +462 -0
- phase_loop_runtime/prompts.py +365 -0
- phase_loop_runtime/provenance.py +165 -0
- phase_loop_runtime/publishing.py +339 -0
- phase_loop_runtime/py.typed +1 -0
- phase_loop_runtime/reconcile.py +1550 -0
- phase_loop_runtime/redaction.py +184 -0
- phase_loop_runtime/reflection_sync.py +293 -0
- phase_loop_runtime/release_guard.py +186 -0
- phase_loop_runtime/render.py +432 -0
- phase_loop_runtime/repo_validation.py +427 -0
- phase_loop_runtime/review_summary.py +150 -0
- phase_loop_runtime/roadmap_lint.py +425 -0
- phase_loop_runtime/route_log.py +59 -0
- phase_loop_runtime/route_policy/__init__.py +11 -0
- phase_loop_runtime/route_policy/fixtures/route_selection.golden.json +263 -0
- phase_loop_runtime/runner.py +8400 -0
- phase_loop_runtime/runtime_paths.py +225 -0
- phase_loop_runtime/runtime_projection.py +113 -0
- phase_loop_runtime/runtime_resources.py +36 -0
- phase_loop_runtime/schema_export.py +292 -0
- phase_loop_runtime/schemas/phase_loop_closeout.schema.json +366 -0
- phase_loop_runtime/skill_install.py +176 -0
- phase_loop_runtime/skill_inventory.py +662 -0
- phase_loop_runtime/skill_paths.py +42 -0
- phase_loop_runtime/skill_sources_plugin.py +37 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-detailed/SKILL.md +152 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/SKILL.md +745 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/claude-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-loop/SKILL.md +115 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/SKILL.md +439 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/claude-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/SKILL.md +334 -0
- phase_loop_runtime/skills_bundle/claude-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/SKILL.md +776 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/claude-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/claude-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/SKILL.md +243 -0
- phase_loop_runtime/skills_bundle/claude-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/SKILL.md +249 -0
- phase_loop_runtime/skills_bundle/claude-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/SKILL.md +74 -0
- phase_loop_runtime/skills_bundle/claude-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-detailed/SKILL.md +154 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/SKILL.md +231 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/codex-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-loop/SKILL.md +248 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/SKILL.md +150 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/codex-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/codex-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/SKILL.md +307 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/codex-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/codex-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/SKILL.md +94 -0
- phase_loop_runtime/skills_bundle/codex-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/SKILL.md +114 -0
- phase_loop_runtime/skills_bundle/codex-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/SKILL.md +102 -0
- phase_loop_runtime/skills_bundle/codex-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/SKILL.md +198 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/gemini-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-loop/SKILL.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/gemini-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/gemini-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/SKILL.md +217 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/gemini-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/gemini-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/gemini-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/gemini-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-board/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-advisor-panel/SKILL.md +38 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-detailed/SKILL.md +144 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/SKILL.md +196 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/allocate_worktree_name.sh +33 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/audit_lane_file_touches.py +197 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/cleanup_lane_worktrees.sh +103 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/parent_tree_leakage_check.sh +125 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/post_merge_import_smoke.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/pre_merge_destructiveness_check.sh +127 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/resolve_branch_from_sha.sh +51 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/state.py +274 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/sweep_stale_worktrees.sh +62 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/team_teardown.sh +32 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/validate_plan_doc.py +642 -0
- phase_loop_runtime/skills_bundle/opencode-execute-phase/scripts/verify_harness.sh +96 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-loop/SKILL.md +113 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/SKILL.md +147 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/assets/review_prompt.md +19 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/parallelization-heuristics.md +111 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/references/roadmap-template.md +193 -0
- phase_loop_runtime/skills_bundle/opencode-phase-roadmap-builder/scripts/validate_roadmap.py +32 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/SKILL.md +119 -0
- phase_loop_runtime/skills_bundle/opencode-plan-detailed/assets/review_prompt.md +25 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/SKILL.md +211 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/assets/review_prompt.md +17 -0
- phase_loop_runtime/skills_bundle/opencode-plan-phase/scripts/validate_plan_doc.py +1092 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-run-train/SKILL.md +75 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/SKILL.md +87 -0
- phase_loop_runtime/skills_bundle/opencode-skill-editor/assets/editor_prompt.md +72 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/SKILL.md +109 -0
- phase_loop_runtime/skills_bundle/opencode-skill-improvement-planner/assets/aggregator_prompt.md +79 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/README.md +1 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/SKILL.md +98 -0
- phase_loop_runtime/skills_bundle/opencode-task-contextualizer/references/task-templates.md +103 -0
- phase_loop_runtime/state.py +156 -0
- phase_loop_runtime/state_degradation.py +136 -0
- phase_loop_runtime/state_ops.py +676 -0
- phase_loop_runtime/train_ledger.py +215 -0
- phase_loop_runtime/train_roadmap.py +339 -0
- phase_loop_runtime/train_runner.py +1197 -0
- phase_loop_runtime/verification_evidence.py +650 -0
- phase_loop_runtime/verification_evidence_validator.py +75 -0
- phase_loop_runtime/visual_evidence_validator.py +36 -0
- phase_loop_runtime/worker_pool.py +123 -0
- phase_loop_runtime/worktree_index.py +270 -0
- phase_loop_runtime-0.2.0.data/data/share/phase-loop-runtime/protocol/protocol.md +2475 -0
- phase_loop_runtime-0.2.0.dist-info/METADATA +106 -0
- phase_loop_runtime-0.2.0.dist-info/RECORD +370 -0
- phase_loop_runtime-0.2.0.dist-info/WHEEL +5 -0
- phase_loop_runtime-0.2.0.dist-info/entry_points.txt +9 -0
- 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
|
+
]
|