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,257 @@
|
|
|
1
|
+
# Advisor Board — Frozen Contracts (Phase 1 ABDFREEZE)
|
|
2
|
+
|
|
3
|
+
Interface-freeze for the model-first, multi-harness Advisor Board
|
|
4
|
+
(`specs/phase-plans-v5.md`). Everything here is **additive and behavior-neutral**:
|
|
5
|
+
no running path changes, `panel_invoker` is untouched, and the `default` board
|
|
6
|
+
reproduces today's 3-leg panel byte-for-byte. Downstream phases (ABDREG,
|
|
7
|
+
ABDRESOLVE, ABDHOME) code against these interfaces so the fan-out integrates
|
|
8
|
+
without a big-bang.
|
|
9
|
+
|
|
10
|
+
Where a contract must reproduce today's behavior, the anchor line in
|
|
11
|
+
`phase-loop-runtime/src/phase_loop_runtime/panel_invoker.py` is cited and the
|
|
12
|
+
equivalence is proven by a test (not asserted in prose).
|
|
13
|
+
|
|
14
|
+
## IF-0-ABDFREEZE-1 — Seat + Board schema (model-first) · `schema.py`, `harness_mapping.py`
|
|
15
|
+
|
|
16
|
+
- **`Seat{model, effort, harness?, lens?, auth?, backing?, host_leg?}`** — a seat
|
|
17
|
+
is a cognition; the harness is a defaulted-but-overridable execution *lane*, not
|
|
18
|
+
the primary key. `effort` is a canonical level in `EFFORT_LEVELS =
|
|
19
|
+
(low, medium, high, max)`. Frozen dataclass with fail-closed validation.
|
|
20
|
+
- **`Board{name, purpose, seats:[Seat], allow_api_key_fallback=false}`** —
|
|
21
|
+
named, purpose-tagged, open-ended seat list; rejects api-key seats unless the
|
|
22
|
+
board opts in.
|
|
23
|
+
- **Config format + location** — `$XDG_CONFIG_HOME/agent-harness/advisor-boards.toml`
|
|
24
|
+
(`board_config_path()`); shape frozen by `fixtures/advisor-boards.example.toml`.
|
|
25
|
+
The loader is ABDREG.
|
|
26
|
+
- **Per-harness model/effort mapping** — `render_seat_invocation(harness, model,
|
|
27
|
+
effort) -> SeatInvocation`. Freezes how `seat.effort` reaches each CLI, incl.
|
|
28
|
+
the **agy/gemini leg where effort is embedded in the model-name string**
|
|
29
|
+
(`render_gemini_model`, panel_invoker.py:1016). Built-3 lanes are concrete;
|
|
30
|
+
breadth lanes raise `EffortMappingError` until ABDREG/ABDHOME/ABDOMNI.
|
|
31
|
+
Round-trip (proven): claude→`--effort max` (:324), codex→
|
|
32
|
+
`-c model_reasoning_effort=xhigh` (:992), gemini→`"Gemini 3.1 Pro (High)"` (:1016).
|
|
33
|
+
- **Seat identity for result re-keying** — `Seat.seat_key` is a stable LABEL over
|
|
34
|
+
every distinguishing field (lane, model, effort, lens), so lens-only-different
|
|
35
|
+
seats get distinct keys. It is not a guaranteed-unique id — a board may hold two
|
|
36
|
+
byte-identical seats — so ABDRESOLVE keys results by **seat position** and uses
|
|
37
|
+
`seat_key` only as the label.
|
|
38
|
+
- **Host-leg identity** — `Seat.host_leg` marker + `identify_host_leg(board,
|
|
39
|
+
HostContext)`. A seat is the native in-process host leg only when the board runs
|
|
40
|
+
*inside* that harness (`HostContext.host_harness`). The standalone runner
|
|
41
|
+
(`host_harness=None`) has no host leg — every leg is a subprocess, exactly as
|
|
42
|
+
today.
|
|
43
|
+
- **Seat → vendor-family projection** — `vendor_family(model, harness)` /
|
|
44
|
+
`seat_vendor_family(seat)`, model-first with a harness-lane fallback.
|
|
45
|
+
Byte-consistent with `governed_review.author_vendor_for_model` (:60-75) and
|
|
46
|
+
`_EXECUTOR_VENDOR` (:47-53). Two same-vendor seats on different harnesses
|
|
47
|
+
(`gpt-5.5` on `codex` and on `opencode`) project to the same family, so the
|
|
48
|
+
governed reviewer≠author disjointness survives model-first. ABDHOME rewires the
|
|
49
|
+
governed gates onto *this* canonical function (not a copy).
|
|
50
|
+
|
|
51
|
+
## IF-0-ABDFREEZE-2 — Registry interfaces + shared fixtures · `registries.py`, `fixtures.py`
|
|
52
|
+
|
|
53
|
+
- **Interfaces (Protocols):** `HarnessRegistry`, `ModelRegistry`,
|
|
54
|
+
`CompatibilityMatrix` with `is_valid(model, harness) -> (bool,
|
|
55
|
+
AuthAvailability)` and `default_lane(model) -> str`.
|
|
56
|
+
- **Frozen return types:** `HarnessSpec`, `ModelSpec`, `AuthAvailability`
|
|
57
|
+
(concrete, so no-silent-key is testable), `MatrixVerdict` alias.
|
|
58
|
+
- **Stubs:** `Stub{Harness,Model}Registry`, `StubCompatibilityMatrix` — raise
|
|
59
|
+
`NotImplementedError`; no six-harness data (that is ABDREG).
|
|
60
|
+
- **Shared canonical fixtures** (the anti-divergence keystone ABDREG populates
|
|
61
|
+
*from* and ABDRESOLVE/ABDHOME test *against*): `DEFAULT_BOARD`, `DEFAULT_SEATS`,
|
|
62
|
+
`CANONICAL_LEG_ORDER`, `CANONICAL_VALID_PAIRS`, `CANONICAL_INVALID_PAIRS`,
|
|
63
|
+
`TWO_SAME_VENDOR_BOARD`.
|
|
64
|
+
|
|
65
|
+
## IF-0-ABDFREEZE-3 — Provider-backing selector + auth enforcement · `backing.py`
|
|
66
|
+
|
|
67
|
+
- **Backing selector** — `select_backing(seat, gateway_available) ->
|
|
68
|
+
BackingDecision`. Per-seat `homebrew | omnigent`; an `omnigent` seat with no
|
|
69
|
+
gateway degrades **skip-with-warning** (fail-closed — never a silent homebrew
|
|
70
|
+
breadth fallback).
|
|
71
|
+
- **Auth = active env scrubbing** — `resolve_seat_env(seat, base_env,
|
|
72
|
+
allow_api_key_fallback)`, freezing the `_subscription_env` pattern
|
|
73
|
+
(panel_invoker.py:226-230,348-353): a subscription seat scrubs **every** vendor
|
|
74
|
+
API-key var; an api-key seat (only behind the board opt-in) scrubs everything
|
|
75
|
+
then injects **only the seat vendor's** key(s). Never silent — an api-key seat
|
|
76
|
+
without the opt-in raises.
|
|
77
|
+
- **`VENDOR_API_KEY_VARS`** — the flat `_API_KEY_VARS` tuple re-keyed by vendor
|
|
78
|
+
family; its union equals today's tuple (proven), so scrubbing stays
|
|
79
|
+
byte-equivalent.
|
|
80
|
+
|
|
81
|
+
## IF-0-ABDFREEZE-4 — Back-compat contract · `fixtures.py` + `tests/test_advisor_board_backcompat.py`
|
|
82
|
+
|
|
83
|
+
- The `default` board (`DEFAULT_BOARD`) resolves to today's three seats in
|
|
84
|
+
`PANEL_LEGS` order, and each seat renders to today's exact model string + effort
|
|
85
|
+
args (cross-checked against `panel_invoker.DEFAULT_LEG_MODELS` and the
|
|
86
|
+
codex/claude/agy arg forms). Full assertions (launch order, payloads, env/auth,
|
|
87
|
+
timeout/retry, result keys, output formatting, failure semantics, `invoke_panel`
|
|
88
|
+
API) land in ABDVERIFY; this ships the **scaffold + the default-board fixture**.
|
|
89
|
+
- `advisor-panel` stays a working alias of `advisor-board` — the rename + alias is
|
|
90
|
+
ABDRESOLVE; this contract only *states* the invariant.
|
|
91
|
+
- **Behavior-neutrality proof:** `git diff` on `panel_invoker.py` is empty and the
|
|
92
|
+
full existing suite is green (this package is purely additive).
|
|
93
|
+
|
|
94
|
+
## IF-0-ABDFREEZE-5 — Observability contract · `events.py`
|
|
95
|
+
|
|
96
|
+
- **Internal envelope** — `AdvisorBoardEvent` (our shape, `EVENT_SCHEMA_VERSION =
|
|
97
|
+
advisor_board.event.v1`, kinds in `EVENT_KINDS`). NOT a guessed Omnigent schema.
|
|
98
|
+
- **launcher ≠ observability-plane** — a natively-launched leg *emits* into a
|
|
99
|
+
forwarded stream; it is never relaunched through the gateway for observability.
|
|
100
|
+
- **Forwarding is async/best-effort and can never delay or fail the native leg** —
|
|
101
|
+
`best_effort_forward(sink, event)` swallows every sink error and never raises;
|
|
102
|
+
`NullSink` keeps the default board a no-op. The mapping to a concrete sink
|
|
103
|
+
(Omnigent v0.4.0 endpoint, or omniagent-plus ui-read-model/state-ledger) is
|
|
104
|
+
deferred to ABDOBS — do not freeze against a guessed upstream schema.
|
|
105
|
+
|
|
106
|
+
## ABDOBS — Observability forwarding (Phase 6) · `observability.py`, `panel_invoker.invoke_board`
|
|
107
|
+
|
|
108
|
+
Builds the mapping ABDFREEZE-5 deferred. **Confirmed sink:** omniagent-plus's own
|
|
109
|
+
`state-ledger` / `ui-read-model` (we control it) — NOT an Omnigent HTTP ingestion
|
|
110
|
+
endpoint. v0.4.0's HTTP surface is launcher-centric and exposes **no** ingestion
|
|
111
|
+
endpoint for an externally-launched (native) session, so a native leg is
|
|
112
|
+
*observed*, never relaunched.
|
|
113
|
+
|
|
114
|
+
- **Envelope → sink mapping** — `map_event_to_runtime_event(event, session_id)`
|
|
115
|
+
and `map_event_to_ledger_record(event, session_id)` project our
|
|
116
|
+
`AdvisorBoardEvent` onto omniagent-plus's *own frozen* wire shapes:
|
|
117
|
+
`runtime_event.v0.1` (`core-contracts/src/events.ts`) inside a
|
|
118
|
+
`state_ledger_record.v0.1`, kind `runtime_event` (`core-contracts/src/state-ledger.ts`)
|
|
119
|
+
— exactly what `AuditLedger.appendRuntimeEvent` (`state-ledger/src/audit-ledger.ts`)
|
|
120
|
+
writes. **A board run projects to a session; each seat projects to a turn.** A
|
|
121
|
+
per-run `sessionId` is minted (`new_session_id()` — never the board name);
|
|
122
|
+
`turnId` derives from the seat's frozen `seat_key` label. `redaction` is
|
|
123
|
+
`metadata_only` (never a raw key; `content_allowed` only for a text delta). A
|
|
124
|
+
`seat.failed` payload conforms to the full `runtime_failure.v0.1` (`errors.ts`)
|
|
125
|
+
— all of schema/category/retryable/actor/scope/message are required upstream.
|
|
126
|
+
NB: the record-level `recordId` / `sequence` are meaningful only for the
|
|
127
|
+
reference `JsonlLedgerWriter`; the real TS `AppendOnlyStore` ASSIGNS them on
|
|
128
|
+
append (its `AppendRecordInput` has no `sequence`), so a real binding overrides
|
|
129
|
+
them. The authoritative, per-session sequence is the `runtime_event` payload's.
|
|
130
|
+
- **Cross-language transport seam** — the ledger is TypeScript, the emit is
|
|
131
|
+
Python, so the boundary is `LedgerWriter` (a Protocol a real omniagent-plus
|
|
132
|
+
binding implements over IPC/HTTP/a shared file) + `JsonlLedgerWriter`, a
|
|
133
|
+
reference transport appending the exact `state_ledger_record.v0.1` records the TS
|
|
134
|
+
`AppendOnlyStore` ingests. We do **not** reimplement ledger internals
|
|
135
|
+
(retention / replay / compaction stay TS-side). This is the integration seam.
|
|
136
|
+
- **Async / best-effort (never delays or fails the native leg)** —
|
|
137
|
+
`AsyncForwardingSink.emit` does a NON-BLOCKING put and returns; a background
|
|
138
|
+
daemon thread does the real (slow / failing) write via `best_effort_forward`.
|
|
139
|
+
The never-**raise** guarantee is frozen in `events.py`; the never-**delay**
|
|
140
|
+
guarantee is here (unbounded queue; a bounded queue drops on full). `BoardObserver`
|
|
141
|
+
wraps construct+map+enqueue in a swallow-all, so even a bad kind / full queue
|
|
142
|
+
never touches the leg. `flush()` / `close()` drain deterministically (tests /
|
|
143
|
+
graceful shutdown only — never on the leg's critical path).
|
|
144
|
+
- **launcher ≠ observability-plane, in code** — every sink here is structurally
|
|
145
|
+
emit-only (no `create_session` / `send_turn`), so the observability path
|
|
146
|
+
*cannot* launch a leg. Combined with the frozen `enforce_native_host_leg`
|
|
147
|
+
(which hard-raises on a gatewayed host leg), the native host leg is observed,
|
|
148
|
+
never relaunched.
|
|
149
|
+
- **Per-workload boundary (documented + enforced)** — `WORKLOAD_BOARD` =
|
|
150
|
+
native launch **+ optional forward** (this path); `WORKLOAD_PHASE_EXECUTION` =
|
|
151
|
+
Omnigent-as-launcher (CS-2.2, out of scope here). `invoke_board(sink=...)` is
|
|
152
|
+
the ONLY opt-in; `sink=None` builds no envelope (default board byte-neutral).
|
|
153
|
+
`invoke_panel` is untouched, so the live default panel is byte-neutral by
|
|
154
|
+
construction.
|
|
155
|
+
- **Key-file note (reviewers):** the phase plan lists
|
|
156
|
+
`agent_runtime_provider.py`; the emit lands in `panel_invoker.invoke_board`
|
|
157
|
+
instead, because the envelope's vocabulary is `board.*` / `seat.*` (with
|
|
158
|
+
`seat_key` / `vendor_family` / `harness`) which only the board seam knows — the
|
|
159
|
+
provider only knows sessions/turns and cannot populate it. `observability.py`
|
|
160
|
+
maps our envelope onto the provider-mirrored `runtime_event.v0.1` shape, so the
|
|
161
|
+
provider layer is still the wire target, just not the emit site.
|
|
162
|
+
|
|
163
|
+
## ABDPRESET — Board preset library + Fable review-path · `presets.py`, `panel_invoker.py`
|
|
164
|
+
|
|
165
|
+
The seven built-in presets (`presets.PRESETS`). Every preset self-validates against
|
|
166
|
+
the real matrix at `load_boards()` time (`tests/test_advisor_board_config.py`,
|
|
167
|
+
`tests/test_advisor_board_integration.py`).
|
|
168
|
+
|
|
169
|
+
- **Review-class = Fable, decoupled from the implementer.** Pre-merge and legal
|
|
170
|
+
review are mid-tier decisions where being wrong is expensive, so the review-class
|
|
171
|
+
boards (`default`, `code-review`, `legal-review`, `legal-strategy-review`) seat
|
|
172
|
+
Fable (`claude-fable-5`) on the claude lane — NOT the implementer model
|
|
173
|
+
`profiles.CLAUDE_IMPLEMENTER_MODEL` (`claude-sonnet-5`). `panel_invoker.DEFAULT_LEG_MODELS["claude"]`
|
|
174
|
+
is the SINGLE source of truth for the panel's default claude model: the claude
|
|
175
|
+
leg builder (`_claude_tui_command`) and the Agent-View attempt both read it, so
|
|
176
|
+
the *legacy* `invoke_panel` path AND the live governed gates
|
|
177
|
+
(`governed_review` / `governed_premerge`, which call `invoke_panel` with no model
|
|
178
|
+
override) review on Fable. `CLAUDE_IMPLEMENTER_MODEL` is untouched — the
|
|
179
|
+
implementer stays Sonnet. The `default` board (`fixtures.DEFAULT_BOARD`) is
|
|
180
|
+
byte-pinned to this Fable `invoke_panel` panel by the golden proof
|
|
181
|
+
(`tests/test_advisor_board_golden.py`); the sole sanctioned delta stays `seat_key`.
|
|
182
|
+
- **`code-review` = three frontier vendors, always.** codex `gpt-5.5`,
|
|
183
|
+
`Gemini 3.1 Pro`, and `claude-fable-5`, each on the `adversarial` lens (supersedes
|
|
184
|
+
the prior two-seat codex+sonnet composition).
|
|
185
|
+
- **Divergent-thinking boards keep Sonnet.** `brainstorm` / `doc-edit` /
|
|
186
|
+
`legal-brainstorm` deliberately retain `claude-sonnet-5` — a diverse voice, a
|
|
187
|
+
low-stakes copyedit, a cheap aggressive ideation seat — where it is the right tool.
|
|
188
|
+
- **Legal boards (`legal-review`, `legal-strategy-review`, `legal-brainstorm`)**
|
|
189
|
+
encode the PRIMARY review lens per seat. `lens` / `purpose` are free-form strings
|
|
190
|
+
(`schema.py`), so the legal lenses/purposes need no enum extension.
|
|
191
|
+
- **Catch-alls for unmodeled tasks (`general`, `solo`).** So the board library is not
|
|
192
|
+
limited to the pre-modeled domains: `general` is the domain-agnostic top-tier PANEL
|
|
193
|
+
(three frontier vendors — gpt-5.5/adversarial, Gemini 3.1 Pro/alternative,
|
|
194
|
+
claude-fable-5/completeness — hand it any task + brief), and `solo` is the
|
|
195
|
+
single-MEMBER form (one `claude-fable-5` seat) for a quick top-end opinion when a
|
|
196
|
+
panel is overkill. A ONE-seat board validates + resolves through `invoke_board` like
|
|
197
|
+
any other (bare/single seats are supported). Both default to TOP-END models: an
|
|
198
|
+
unanticipated task cannot be assumed low-stakes, so the safe default is frontier —
|
|
199
|
+
dial down explicitly (a cheaper board) when a task is known-cheap. Their lenses
|
|
200
|
+
(`adversarial`/`alternative`/`completeness`) and purpose (`general`) are free-form
|
|
201
|
+
strings, so no enum extension.
|
|
202
|
+
- **Deep-seat FOLLOW-ON (documented, NOT built here).** The richer legal treatment —
|
|
203
|
+
four lenses per seat, an apex-Opus (`claude-opus-4-8`) seat, a verify-round, and
|
|
204
|
+
retrieval-grounded citation-verification — is a deliberate follow-on. The current
|
|
205
|
+
legal boards ship the single-primary-lens-per-seat form; the deep-seat form layers
|
|
206
|
+
onto the same seat/board schema (no schema change) when built.
|
|
207
|
+
|
|
208
|
+
## ABDPAR — Concurrent leg execution · `panel_invoker.invoke_board` / `invoke_panel`
|
|
209
|
+
|
|
210
|
+
`invoke_board` and `invoke_panel` fan their seats/legs out across a bounded
|
|
211
|
+
`ThreadPoolExecutor` (`_run_legs_ordered`), not a sequential loop — legs are blocking
|
|
212
|
+
subprocess I/O (the CLI wait releases the GIL), so wall-clock is `~max(leg)`, not
|
|
213
|
+
`sum(leg)`.
|
|
214
|
+
|
|
215
|
+
- **Parallel is the DEFAULT (opt-out, not opt-in).** Both entry points take a single
|
|
216
|
+
`max_concurrency: int | None = None` knob, threaded through the governed gates
|
|
217
|
+
(`governed_planning_gate` → `run_governed_premerge_loop` → `governed_premerge_for_run`)
|
|
218
|
+
so a caller CAN request sequential, while the default everywhere stays parallel:
|
|
219
|
+
- `None` (default) → parallel, bounded by `min(len(seats), 8)`.
|
|
220
|
+
- `1` → sequential (the opt-in escape hatch — debugging, a rate-limited / throttled
|
|
221
|
+
provider, a constrained host).
|
|
222
|
+
- `N` → cap concurrency at `N`.
|
|
223
|
+
It is the SAME thread-pool path: `max_workers = max(1, min(max_concurrency or len(seats), 8))`,
|
|
224
|
+
so `max_concurrency=1` degrades to one worker (strictly serial) with no separate
|
|
225
|
+
sequential branch. `max_concurrency` is a keyword-only, default-valued additive
|
|
226
|
+
extension to the frozen `invoke_panel` signature (back-compat, like `models` #66).
|
|
227
|
+
|
|
228
|
+
Frozen invariants (concurrency is a **timing-only** change — never a leg's outcome;
|
|
229
|
+
the golden proves byte-identity):
|
|
230
|
+
|
|
231
|
+
- **Positional order** — futures are submitted in seat/leg order and read back by
|
|
232
|
+
index, so `result[i]` corresponds to `seats[i]`/`legs[i]` regardless of finish
|
|
233
|
+
order. `resolver.key_results_by_seat` re-keys by position and the golden asserts
|
|
234
|
+
order + content, so this is load-bearing.
|
|
235
|
+
- **Fail-closed per seat** — the extracted per-seat/per-leg body returns a DEGRADED
|
|
236
|
+
`PanelLegResult` on any exception, so a future's `.result()` never raises and one
|
|
237
|
+
broken leg can never crash the pool or the board.
|
|
238
|
+
- **Single shared reads before the pool** — the one `GET /v1/harnesses` gateway-catalog
|
|
239
|
+
fetch and the seat-validation/host-leg checks stay ABOVE the pool (one fetch, shared
|
|
240
|
+
read); the observability emit stays AFTER, in seat order. Bounded `max_workers =
|
|
241
|
+
min(len(seats), 8)`.
|
|
242
|
+
- **Proof** — a `threading.Barrier(N)` test proves both directions without real sleeps:
|
|
243
|
+
the DEFAULT satisfies the barrier (all N legs in-flight at once → all OK), and
|
|
244
|
+
`max_concurrency=1` makes the same barrier unsatisfiable (one worker → it times out →
|
|
245
|
+
fail-closed DEGRADED), so the pair discriminates parallel from sequential. A third
|
|
246
|
+
test confirms sequential mode returns byte-identical ordered results.
|
|
247
|
+
- **Thread-safety of the real leg paths** — the leg execs install NO signal handlers or
|
|
248
|
+
timers (`signal.signal` / `alarm` / `setitimer` would raise `ValueError: signal only
|
|
249
|
+
works in main thread` off the main thread); the only `signal.` use is `os.killpg(pid,
|
|
250
|
+
SIGTERM/SIGKILL)` (a constant, thread-safe), and timeouts run on `select.select(...)`
|
|
251
|
+
/ `subprocess.run(timeout=...)`, not `SIGALRM`. Each leg stages its own temp review
|
|
252
|
+
dir, so concurrent legs never share filesystem state. Verified: the real `spawn=None`
|
|
253
|
+
path runs inside worker threads with no signal-in-thread error.
|
|
254
|
+
- **Known edge (untested):** a custom board with two seats on the SAME lane (e.g. two
|
|
255
|
+
`claude` seats) now runs two concurrent same-lane CLI/PTY sessions — a scenario this
|
|
256
|
+
fan-out newly enables and nothing yet tests. The `default` board has one claude seat,
|
|
257
|
+
so it is unaffected.
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""Advisor Board — frozen contracts (Phase 1 ABDFREEZE).
|
|
2
|
+
|
|
3
|
+
Interface-only preamble for the model-first, multi-harness Advisor Board
|
|
4
|
+
(specs/phase-plans-v5.md). This package ships the typed contracts + importable
|
|
5
|
+
stubs + shared canonical fixtures the parallel fan-out (ABDREG / ABDRESOLVE /
|
|
6
|
+
ABDHOME) codes against. It is ADDITIVE and behavior-neutral: nothing here touches
|
|
7
|
+
the running ``panel_invoker`` path.
|
|
8
|
+
|
|
9
|
+
Frozen gates (see ``CONTRACTS.md``):
|
|
10
|
+
* IF-0-ABDFREEZE-1 — seat/board schema, config location, per-harness model/effort
|
|
11
|
+
mapping, host-leg identity, seat -> vendor-family projection (``schema`` +
|
|
12
|
+
``harness_mapping``).
|
|
13
|
+
* IF-0-ABDFREEZE-2 — registry interfaces + (model x harness) matrix API + shared
|
|
14
|
+
canonical fixtures (``registries`` + ``fixtures``).
|
|
15
|
+
* IF-0-ABDFREEZE-3 — provider-backing selector + auth-enforcement (active env
|
|
16
|
+
scrubbing) contract (``backing``).
|
|
17
|
+
* IF-0-ABDFREEZE-4 — back-compat: the ``default`` board reproduces today's 3-leg
|
|
18
|
+
behavior (``fixtures.DEFAULT_BOARD`` + the golden-test scaffold).
|
|
19
|
+
* IF-0-ABDFREEZE-5 — internal advisor-board event envelope + best-effort
|
|
20
|
+
forwarding (``events``); its concrete sink mapping lands in ABDOBS.
|
|
21
|
+
|
|
22
|
+
ABDOBS (Phase 6) then adds ``observability``: the envelope → omniagent-plus
|
|
23
|
+
state-ledger (``state_ledger_record.v0.1`` / ``runtime_event.v0.1``) mapping, an
|
|
24
|
+
:class:`~observability.AsyncForwardingSink` for off-thread best-effort dispatch,
|
|
25
|
+
and the ``LedgerWriter`` cross-language transport seam. This is the one place the
|
|
26
|
+
package touches ``panel_invoker`` — an OPT-IN ``sink=`` on ``invoke_board``;
|
|
27
|
+
``sink=None`` stays byte-neutral.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from .schema import (
|
|
32
|
+
AUTH_API_KEY,
|
|
33
|
+
AUTH_LANES,
|
|
34
|
+
AUTH_SUBSCRIPTION,
|
|
35
|
+
BACKING_HOMEBREW,
|
|
36
|
+
BACKING_OMNIGENT,
|
|
37
|
+
CONFIG_RELATIVE_PATH,
|
|
38
|
+
EFFORT_LEVELS,
|
|
39
|
+
PROVIDER_BACKINGS,
|
|
40
|
+
Board,
|
|
41
|
+
HostContext,
|
|
42
|
+
Seat,
|
|
43
|
+
board_config_path,
|
|
44
|
+
identify_host_leg,
|
|
45
|
+
seat_vendor_family,
|
|
46
|
+
vendor_family,
|
|
47
|
+
vendor_of_harness,
|
|
48
|
+
vendor_of_model,
|
|
49
|
+
)
|
|
50
|
+
from .harness_mapping import (
|
|
51
|
+
EffortMappingError,
|
|
52
|
+
MECH_CONFIG,
|
|
53
|
+
MECH_FLAG,
|
|
54
|
+
MECH_MODEL_NAME,
|
|
55
|
+
SeatInvocation,
|
|
56
|
+
gemini_base_model,
|
|
57
|
+
render_gemini_model,
|
|
58
|
+
render_seat_invocation,
|
|
59
|
+
)
|
|
60
|
+
from .registries import (
|
|
61
|
+
DEFAULT_HARNESS_REGISTRY,
|
|
62
|
+
DEFAULT_MODEL_REGISTRY,
|
|
63
|
+
AuthAvailability,
|
|
64
|
+
CompatibilityMatrix,
|
|
65
|
+
DefaultHarnessRegistry,
|
|
66
|
+
DefaultModelRegistry,
|
|
67
|
+
HarnessRegistry,
|
|
68
|
+
HarnessSpec,
|
|
69
|
+
MatrixVerdict,
|
|
70
|
+
ModelRegistry,
|
|
71
|
+
ModelSpec,
|
|
72
|
+
StubCompatibilityMatrix,
|
|
73
|
+
StubHarnessRegistry,
|
|
74
|
+
StubModelRegistry,
|
|
75
|
+
UnknownHarnessError,
|
|
76
|
+
UnknownModelError,
|
|
77
|
+
)
|
|
78
|
+
from .matrix import (
|
|
79
|
+
DefaultCompatibilityMatrix,
|
|
80
|
+
default_matrix,
|
|
81
|
+
)
|
|
82
|
+
from .presets import (
|
|
83
|
+
DEFAULT_BOARD_NAME,
|
|
84
|
+
PRESET_NAMES,
|
|
85
|
+
PRESETS,
|
|
86
|
+
get_preset,
|
|
87
|
+
)
|
|
88
|
+
from .config import (
|
|
89
|
+
BoardConfig,
|
|
90
|
+
BoardConfigError,
|
|
91
|
+
load_boards,
|
|
92
|
+
)
|
|
93
|
+
from .backing import (
|
|
94
|
+
VENDOR_API_KEY_VARS,
|
|
95
|
+
BackingDecision,
|
|
96
|
+
all_vendor_key_vars,
|
|
97
|
+
resolve_seat_env,
|
|
98
|
+
select_backing,
|
|
99
|
+
)
|
|
100
|
+
from .backing_omnigent import (
|
|
101
|
+
OMNIGENT_FREEZE_TARGET,
|
|
102
|
+
HARNESS_ENDPOINT,
|
|
103
|
+
SESSION_ENDPOINTS,
|
|
104
|
+
VENDOR_KEY_HEADER_PREFIX,
|
|
105
|
+
OmnigentBacking,
|
|
106
|
+
OmnigentGatewayUnavailable,
|
|
107
|
+
OmnigentHttpClient,
|
|
108
|
+
OmnigentHttpError,
|
|
109
|
+
SeatRunOutcome,
|
|
110
|
+
classify_http_failure,
|
|
111
|
+
)
|
|
112
|
+
from .events import (
|
|
113
|
+
EVENT_KINDS,
|
|
114
|
+
EVENT_SCHEMA_VERSION,
|
|
115
|
+
AdvisorBoardEvent,
|
|
116
|
+
EventSink,
|
|
117
|
+
NullSink,
|
|
118
|
+
best_effort_forward,
|
|
119
|
+
)
|
|
120
|
+
from .observability import (
|
|
121
|
+
LEDGER_RECORD_KIND,
|
|
122
|
+
LEDGER_RECORD_SCHEMA,
|
|
123
|
+
RUNTIME_EVENT_SCHEMA,
|
|
124
|
+
RUNTIME_FAILURE_SCHEMA,
|
|
125
|
+
WORKLOAD_BOARD,
|
|
126
|
+
WORKLOAD_PHASE_EXECUTION,
|
|
127
|
+
AsyncForwardingSink,
|
|
128
|
+
BoardObserver,
|
|
129
|
+
CollectingSink,
|
|
130
|
+
JsonlLedgerWriter,
|
|
131
|
+
LedgerWriter,
|
|
132
|
+
StateLedgerSink,
|
|
133
|
+
map_event_to_ledger_record,
|
|
134
|
+
map_event_to_runtime_event,
|
|
135
|
+
new_session_id,
|
|
136
|
+
)
|
|
137
|
+
from .fixtures import (
|
|
138
|
+
CANONICAL_INVALID_PAIRS,
|
|
139
|
+
CANONICAL_LEG_ORDER,
|
|
140
|
+
CANONICAL_VALID_PAIRS,
|
|
141
|
+
DEFAULT_BOARD,
|
|
142
|
+
DEFAULT_SEAT_EFFORT_ARGS,
|
|
143
|
+
DEFAULT_SEAT_RENDERED_MODEL,
|
|
144
|
+
DEFAULT_SEATS,
|
|
145
|
+
TWO_SAME_VENDOR_BOARD,
|
|
146
|
+
)
|
|
147
|
+
from .resolver import (
|
|
148
|
+
BOARD_ALIASES,
|
|
149
|
+
STANDIN_BOARDS,
|
|
150
|
+
BoardResolutionError,
|
|
151
|
+
BoardResolver,
|
|
152
|
+
SeatSpecError,
|
|
153
|
+
key_results_by_seat,
|
|
154
|
+
parse_seat_spec,
|
|
155
|
+
parse_seats,
|
|
156
|
+
resolve_board,
|
|
157
|
+
seat_result_key,
|
|
158
|
+
)
|
|
159
|
+
from .validation import (
|
|
160
|
+
SeatValidationError,
|
|
161
|
+
SeatVerdict,
|
|
162
|
+
validate_board,
|
|
163
|
+
validate_seat,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
__all__ = [
|
|
167
|
+
# schema
|
|
168
|
+
"Seat",
|
|
169
|
+
"Board",
|
|
170
|
+
"HostContext",
|
|
171
|
+
"EFFORT_LEVELS",
|
|
172
|
+
"AUTH_LANES",
|
|
173
|
+
"AUTH_SUBSCRIPTION",
|
|
174
|
+
"AUTH_API_KEY",
|
|
175
|
+
"PROVIDER_BACKINGS",
|
|
176
|
+
"BACKING_HOMEBREW",
|
|
177
|
+
"BACKING_OMNIGENT",
|
|
178
|
+
"CONFIG_RELATIVE_PATH",
|
|
179
|
+
"board_config_path",
|
|
180
|
+
"identify_host_leg",
|
|
181
|
+
"seat_vendor_family",
|
|
182
|
+
"vendor_family",
|
|
183
|
+
"vendor_of_harness",
|
|
184
|
+
"vendor_of_model",
|
|
185
|
+
# harness mapping
|
|
186
|
+
"SeatInvocation",
|
|
187
|
+
"render_seat_invocation",
|
|
188
|
+
"render_gemini_model",
|
|
189
|
+
"gemini_base_model",
|
|
190
|
+
"EffortMappingError",
|
|
191
|
+
"MECH_FLAG",
|
|
192
|
+
"MECH_CONFIG",
|
|
193
|
+
"MECH_MODEL_NAME",
|
|
194
|
+
# registries
|
|
195
|
+
"HarnessSpec",
|
|
196
|
+
"ModelSpec",
|
|
197
|
+
"AuthAvailability",
|
|
198
|
+
"MatrixVerdict",
|
|
199
|
+
"HarnessRegistry",
|
|
200
|
+
"ModelRegistry",
|
|
201
|
+
"CompatibilityMatrix",
|
|
202
|
+
"StubHarnessRegistry",
|
|
203
|
+
"StubModelRegistry",
|
|
204
|
+
"StubCompatibilityMatrix",
|
|
205
|
+
# registries — populated (ABDREG)
|
|
206
|
+
"DefaultHarnessRegistry",
|
|
207
|
+
"DefaultModelRegistry",
|
|
208
|
+
"DEFAULT_HARNESS_REGISTRY",
|
|
209
|
+
"DEFAULT_MODEL_REGISTRY",
|
|
210
|
+
"UnknownHarnessError",
|
|
211
|
+
"UnknownModelError",
|
|
212
|
+
# matrix (ABDREG) — seat/board validation is exported from validation below
|
|
213
|
+
"DefaultCompatibilityMatrix",
|
|
214
|
+
"default_matrix",
|
|
215
|
+
# presets + config loader (ABDREG)
|
|
216
|
+
"PRESETS",
|
|
217
|
+
"PRESET_NAMES",
|
|
218
|
+
"DEFAULT_BOARD_NAME",
|
|
219
|
+
"get_preset",
|
|
220
|
+
"BoardConfig",
|
|
221
|
+
"BoardConfigError",
|
|
222
|
+
"load_boards",
|
|
223
|
+
# backing + auth
|
|
224
|
+
"VENDOR_API_KEY_VARS",
|
|
225
|
+
"all_vendor_key_vars",
|
|
226
|
+
"BackingDecision",
|
|
227
|
+
"select_backing",
|
|
228
|
+
"resolve_seat_env",
|
|
229
|
+
# backing — omnigent transport (ABDOMNI)
|
|
230
|
+
"OMNIGENT_FREEZE_TARGET",
|
|
231
|
+
"HARNESS_ENDPOINT",
|
|
232
|
+
"SESSION_ENDPOINTS",
|
|
233
|
+
"VENDOR_KEY_HEADER_PREFIX",
|
|
234
|
+
"OmnigentBacking",
|
|
235
|
+
"OmnigentHttpClient",
|
|
236
|
+
"OmnigentHttpError",
|
|
237
|
+
"OmnigentGatewayUnavailable",
|
|
238
|
+
"SeatRunOutcome",
|
|
239
|
+
"classify_http_failure",
|
|
240
|
+
# events
|
|
241
|
+
"AdvisorBoardEvent",
|
|
242
|
+
"EventSink",
|
|
243
|
+
"NullSink",
|
|
244
|
+
"best_effort_forward",
|
|
245
|
+
"EVENT_KINDS",
|
|
246
|
+
"EVENT_SCHEMA_VERSION",
|
|
247
|
+
# observability (ABDOBS)
|
|
248
|
+
"WORKLOAD_BOARD",
|
|
249
|
+
"WORKLOAD_PHASE_EXECUTION",
|
|
250
|
+
"LEDGER_RECORD_SCHEMA",
|
|
251
|
+
"RUNTIME_EVENT_SCHEMA",
|
|
252
|
+
"RUNTIME_FAILURE_SCHEMA",
|
|
253
|
+
"LEDGER_RECORD_KIND",
|
|
254
|
+
"new_session_id",
|
|
255
|
+
"map_event_to_runtime_event",
|
|
256
|
+
"map_event_to_ledger_record",
|
|
257
|
+
"LedgerWriter",
|
|
258
|
+
"JsonlLedgerWriter",
|
|
259
|
+
"StateLedgerSink",
|
|
260
|
+
"CollectingSink",
|
|
261
|
+
"AsyncForwardingSink",
|
|
262
|
+
"BoardObserver",
|
|
263
|
+
# fixtures
|
|
264
|
+
"DEFAULT_BOARD",
|
|
265
|
+
"DEFAULT_SEATS",
|
|
266
|
+
"DEFAULT_SEAT_RENDERED_MODEL",
|
|
267
|
+
"DEFAULT_SEAT_EFFORT_ARGS",
|
|
268
|
+
"CANONICAL_LEG_ORDER",
|
|
269
|
+
"CANONICAL_VALID_PAIRS",
|
|
270
|
+
"CANONICAL_INVALID_PAIRS",
|
|
271
|
+
"TWO_SAME_VENDOR_BOARD",
|
|
272
|
+
# resolver (ABDRESOLVE)
|
|
273
|
+
"BoardResolver",
|
|
274
|
+
"resolve_board",
|
|
275
|
+
"parse_seat_spec",
|
|
276
|
+
"parse_seats",
|
|
277
|
+
"SeatSpecError",
|
|
278
|
+
"BoardResolutionError",
|
|
279
|
+
"BOARD_ALIASES",
|
|
280
|
+
"STANDIN_BOARDS",
|
|
281
|
+
"seat_result_key",
|
|
282
|
+
"key_results_by_seat",
|
|
283
|
+
# validation (ABDRESOLVE)
|
|
284
|
+
"validate_seat",
|
|
285
|
+
"validate_board",
|
|
286
|
+
"SeatValidationError",
|
|
287
|
+
"SeatVerdict",
|
|
288
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Provider-backing selector + auth-enforcement contract (IF-0-ABDFREEZE-3).
|
|
2
|
+
|
|
3
|
+
Two orthogonal per-seat axes, frozen here:
|
|
4
|
+
|
|
5
|
+
* **backing** (``homebrew | omnigent``) — the transport. Selected per seat;
|
|
6
|
+
``homebrew`` is the default and keeps the built-3 + native host leg.
|
|
7
|
+
* **auth** (``subscription | api_key``) — the credential lane. Subscription is
|
|
8
|
+
the default; api-key is reachable only behind ``Board.allow_api_key_fallback``.
|
|
9
|
+
|
|
10
|
+
**No-silent-key is enforced by ACTIVE environment scrubbing**, freezing the
|
|
11
|
+
existing ``_subscription_env`` pattern (panel_invoker.py:226-230,348-353) into a
|
|
12
|
+
per-seat, vendor-keyed contract:
|
|
13
|
+
|
|
14
|
+
* a **subscription** seat scrubs EVERY vendor API-key var from the subprocess env
|
|
15
|
+
/ gateway payload (identical to ``_subscription_env`` today); and
|
|
16
|
+
* an **api-key fallback** seat scrubs everything, then injects ONLY the seat
|
|
17
|
+
vendor's key var(s) — never another vendor's, never silently.
|
|
18
|
+
|
|
19
|
+
``VENDOR_API_KEY_VARS`` is the flat ``panel_invoker._API_KEY_VARS`` tuple
|
|
20
|
+
re-expressed keyed by vendor family, so "inject only the seat vendor's key" is
|
|
21
|
+
expressible. The union of its values equals the current flat tuple (asserted in
|
|
22
|
+
``tests/test_advisor_board_backcompat.py``), so scrubbing stays byte-equivalent.
|
|
23
|
+
The reference env functions below are pure and importable; ABDHOME wires them
|
|
24
|
+
into the real launch/gateway env (this module changes no running path).
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
from collections.abc import Mapping
|
|
29
|
+
from dataclasses import dataclass
|
|
30
|
+
|
|
31
|
+
from .schema import (
|
|
32
|
+
AUTH_API_KEY,
|
|
33
|
+
AUTH_SUBSCRIPTION,
|
|
34
|
+
BACKING_HOMEBREW,
|
|
35
|
+
BACKING_OMNIGENT,
|
|
36
|
+
PROVIDER_BACKINGS,
|
|
37
|
+
Seat,
|
|
38
|
+
seat_vendor_family,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# Vendor family -> the provider API-key env var(s) that vendor authenticates with.
|
|
43
|
+
# The UNION of all values MUST equal ``panel_invoker._API_KEY_VARS`` so subscription
|
|
44
|
+
# scrubbing is byte-equivalent to ``_subscription_env`` today.
|
|
45
|
+
VENDOR_API_KEY_VARS: dict[str, tuple[str, ...]] = {
|
|
46
|
+
"codex": ("OPENAI_API_KEY",),
|
|
47
|
+
"claude": ("ANTHROPIC_API_KEY",),
|
|
48
|
+
"gemini": ("GEMINI_API_KEY", "GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"),
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def all_vendor_key_vars() -> tuple[str, ...]:
|
|
53
|
+
"""Every vendor API-key var (scrub set for a subscription seat)."""
|
|
54
|
+
seen: list[str] = []
|
|
55
|
+
for vars_ in VENDOR_API_KEY_VARS.values():
|
|
56
|
+
for var in vars_:
|
|
57
|
+
if var not in seen:
|
|
58
|
+
seen.append(var)
|
|
59
|
+
return tuple(seen)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# --- backing selector -------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class BackingDecision:
|
|
67
|
+
"""Which transport a seat resolves to, and why. Fail-closed: an ``omnigent``
|
|
68
|
+
seat whose gateway is unavailable degrades to ``skip`` (never a silent
|
|
69
|
+
homebrew fallback for a breadth harness — hand-writing breadth defeats the
|
|
70
|
+
Omnigent maintenance-offload, per ABDHOME non-goals)."""
|
|
71
|
+
|
|
72
|
+
backing: str
|
|
73
|
+
skip: bool = False
|
|
74
|
+
reason: str = ""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def select_backing(seat: Seat, *, gateway_available: bool = True) -> BackingDecision:
|
|
78
|
+
"""Freeze the selector contract: honor ``seat.backing``; an ``omnigent`` seat
|
|
79
|
+
with no gateway degrades skip-with-warning. ABDHOME/ABDOMNI supply the real
|
|
80
|
+
availability probes; this fixes the DECISION SHAPE they return."""
|
|
81
|
+
if seat.backing not in PROVIDER_BACKINGS:
|
|
82
|
+
raise ValueError(f"seat.backing {seat.backing!r} not in {PROVIDER_BACKINGS}")
|
|
83
|
+
if seat.backing == BACKING_OMNIGENT and not gateway_available:
|
|
84
|
+
return BackingDecision(BACKING_OMNIGENT, skip=True, reason="omnigent gateway unavailable")
|
|
85
|
+
return BackingDecision(seat.backing)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# --- auth enforcement (active env scrubbing) --------------------------------
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def resolve_seat_env(
|
|
92
|
+
seat: Seat,
|
|
93
|
+
base_env: Mapping[str, str],
|
|
94
|
+
*,
|
|
95
|
+
allow_api_key_fallback: bool = False,
|
|
96
|
+
) -> dict[str, str]:
|
|
97
|
+
"""Reference implementation of the frozen no-silent-key env contract.
|
|
98
|
+
|
|
99
|
+
* Always scrub EVERY vendor API-key var from a copy of ``base_env`` (the
|
|
100
|
+
``_subscription_env`` behavior — a subscription seat ends here).
|
|
101
|
+
* For an ``api_key`` seat, and ONLY when the board opts in
|
|
102
|
+
(``allow_api_key_fallback``), re-inject ONLY the seat vendor's key var(s)
|
|
103
|
+
from ``base_env``. Any other lane / a disallowed board leaves the env
|
|
104
|
+
scrubbed (fail-closed).
|
|
105
|
+
|
|
106
|
+
Raises when a seat requests the api-key lane without the board opt-in, so a
|
|
107
|
+
silent key can never slip through. Pure and side-effect-free; ABDHOME wires
|
|
108
|
+
it into the subprocess/gateway env.
|
|
109
|
+
"""
|
|
110
|
+
env = {k: v for k, v in base_env.items() if k not in all_vendor_key_vars()}
|
|
111
|
+
if seat.auth == AUTH_SUBSCRIPTION:
|
|
112
|
+
return env
|
|
113
|
+
if seat.auth == AUTH_API_KEY:
|
|
114
|
+
if not allow_api_key_fallback:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
f"seat {seat.seat_key} requests the api_key lane but the board did not "
|
|
117
|
+
"opt in (allow_api_key_fallback=False) — never-silent-key"
|
|
118
|
+
)
|
|
119
|
+
vendor = seat_vendor_family(seat)
|
|
120
|
+
for var in VENDOR_API_KEY_VARS.get(vendor, ()): # ONLY this vendor's key(s)
|
|
121
|
+
if var in base_env:
|
|
122
|
+
env[var] = base_env[var]
|
|
123
|
+
return env
|
|
124
|
+
raise ValueError(f"unknown seat.auth {seat.auth!r}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
__all__ = [
|
|
128
|
+
"VENDOR_API_KEY_VARS",
|
|
129
|
+
"all_vendor_key_vars",
|
|
130
|
+
"BackingDecision",
|
|
131
|
+
"select_backing",
|
|
132
|
+
"resolve_seat_env",
|
|
133
|
+
"AUTH_SUBSCRIPTION",
|
|
134
|
+
"AUTH_API_KEY",
|
|
135
|
+
"BACKING_HOMEBREW",
|
|
136
|
+
"BACKING_OMNIGENT",
|
|
137
|
+
]
|