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,253 @@
|
|
|
1
|
+
"""Board resolution — name -> seats, settable default, ad-hoc seats (ABDRESOLVE lane 1).
|
|
2
|
+
|
|
3
|
+
Turns a board *reference* into a concrete ``Board`` of seats:
|
|
4
|
+
|
|
5
|
+
* a **name** (``"default"``, ``"code-review"``, ...) -> the named board from the
|
|
6
|
+
catalog;
|
|
7
|
+
* **nothing** -> the settable *default* board (bare ``advisor-board``);
|
|
8
|
+
* an **ad-hoc** ``--seats model:effort[:harness]`` spec -> a synthesized board.
|
|
9
|
+
|
|
10
|
+
Coded against the frozen registry/matrix interface (``registries``); the harness
|
|
11
|
+
lane for a bare ``model:effort`` seat is resolved via
|
|
12
|
+
``CompatibilityMatrix.default_lane``. Until ABDREG ships the config loader +
|
|
13
|
+
presets, the catalog defaults to a **stand-in** built from the shared canonical
|
|
14
|
+
fixtures (``STANDIN_BOARDS``) so ABDRESOLVE resolves ``default`` and
|
|
15
|
+
``code-review`` without depending on ABDREG's live output (integration at
|
|
16
|
+
ABDVERIFY). A caller (or ABDREG) injects the real catalog + matrix.
|
|
17
|
+
|
|
18
|
+
``advisor-panel`` remains a working alias of the default board
|
|
19
|
+
(``BOARD_ALIASES``) so the historical name keeps resolving after the
|
|
20
|
+
advisor-panel -> advisor-board rename.
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from collections.abc import Iterable, Mapping, Sequence
|
|
25
|
+
|
|
26
|
+
from .fixtures import DEFAULT_BOARD
|
|
27
|
+
from .registries import CompatibilityMatrix
|
|
28
|
+
from .schema import EFFORT_LEVELS, Board, Seat
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SeatSpecError(ValueError):
|
|
32
|
+
"""A ``--seats model:effort[:harness]`` token is malformed."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BoardResolutionError(KeyError):
|
|
36
|
+
"""A board reference names no board in the catalog (and is not an alias)."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, name: str, available: Sequence[str]) -> None:
|
|
39
|
+
self.name = name
|
|
40
|
+
self.available = tuple(available)
|
|
41
|
+
joined = ", ".join(sorted(self.available)) or "(none)"
|
|
42
|
+
super().__init__(
|
|
43
|
+
f"unknown board {name!r}; available boards: {joined} "
|
|
44
|
+
"(ad-hoc seats: --seats model:effort[:harness])"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def __str__(self) -> str: # KeyError repr wraps the message in quotes otherwise
|
|
48
|
+
return self.args[0]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# The historical panel name resolves to the default premerge-review board, so
|
|
52
|
+
# existing agent instructions that say "advisor-panel" keep working after the
|
|
53
|
+
# advisor-panel -> advisor-board rename.
|
|
54
|
+
BOARD_ALIASES: dict[str, str] = {
|
|
55
|
+
"advisor-panel": "default",
|
|
56
|
+
"panel": "default",
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# --- ad-hoc seat parsing ----------------------------------------------------
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def parse_seat_spec(spec: str, *, matrix: CompatibilityMatrix | None = None) -> Seat:
|
|
64
|
+
"""Parse one ``model:effort[:harness]`` token into a ``Seat``.
|
|
65
|
+
|
|
66
|
+
``model`` required, the primary key.
|
|
67
|
+
``effort`` required, a canonical level in ``EFFORT_LEVELS``.
|
|
68
|
+
``harness`` optional; when omitted the lane is resolved via
|
|
69
|
+
``matrix.default_lane(model)`` (frozen interface). With no matrix
|
|
70
|
+
the seat is left lane-unresolved (``harness=None``) for the
|
|
71
|
+
validation/backing phase to fill.
|
|
72
|
+
"""
|
|
73
|
+
raw = (spec or "").strip()
|
|
74
|
+
if not raw:
|
|
75
|
+
raise SeatSpecError("empty seat spec; expected model:effort[:harness]")
|
|
76
|
+
parts = raw.split(":")
|
|
77
|
+
if len(parts) < 2 or len(parts) > 3:
|
|
78
|
+
raise SeatSpecError(
|
|
79
|
+
f"seat spec {spec!r} must be model:effort[:harness] (1 or 2 colons)"
|
|
80
|
+
)
|
|
81
|
+
model = parts[0].strip()
|
|
82
|
+
effort = parts[1].strip().lower()
|
|
83
|
+
harness = parts[2].strip() if len(parts) == 3 and parts[2].strip() else None
|
|
84
|
+
if not model:
|
|
85
|
+
raise SeatSpecError(f"seat spec {spec!r} has an empty model")
|
|
86
|
+
if effort not in EFFORT_LEVELS:
|
|
87
|
+
raise SeatSpecError(
|
|
88
|
+
f"seat spec {spec!r} effort {effort!r} not in {EFFORT_LEVELS}"
|
|
89
|
+
)
|
|
90
|
+
if harness is None and matrix is not None:
|
|
91
|
+
harness = matrix.default_lane(model)
|
|
92
|
+
return Seat(model=model, effort=effort, harness=harness)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def parse_seats(
|
|
96
|
+
specs: str | Iterable[str], *, matrix: CompatibilityMatrix | None = None
|
|
97
|
+
) -> tuple[Seat, ...]:
|
|
98
|
+
"""Parse a comma-separated string OR an iterable of ``model:effort[:harness]``
|
|
99
|
+
tokens into an ordered tuple of seats (order preserved for result re-keying)."""
|
|
100
|
+
if isinstance(specs, str):
|
|
101
|
+
tokens = [tok for tok in (t.strip() for t in specs.split(",")) if tok]
|
|
102
|
+
else:
|
|
103
|
+
tokens = [str(tok).strip() for tok in specs if str(tok).strip()]
|
|
104
|
+
if not tokens:
|
|
105
|
+
raise SeatSpecError("no seats parsed; expected model:effort[:harness], ...")
|
|
106
|
+
return tuple(parse_seat_spec(tok, matrix=matrix) for tok in tokens)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# --- stand-in board catalog (until ABDREG's config loader + presets) --------
|
|
110
|
+
|
|
111
|
+
# A stand-in ``code-review`` preset mirroring ``fixtures/advisor-boards.example.toml``
|
|
112
|
+
# so ``advisor-board --board code-review`` resolved in ABDRESOLVE before the config
|
|
113
|
+
# loader landed. SUPERSEDED by ``presets.PRESETS`` / ``config.load_boards`` (which
|
|
114
|
+
# ship the seven real presets — the live ``code-review`` is now three frontier
|
|
115
|
+
# adversarial seats on Fable, not this two-seat mirror); kept only as the ABDRESOLVE
|
|
116
|
+
# stand-in fixture, off every live path.
|
|
117
|
+
_STANDIN_CODE_REVIEW = Board(
|
|
118
|
+
name="code-review",
|
|
119
|
+
purpose="code-review",
|
|
120
|
+
seats=(
|
|
121
|
+
Seat(model="gpt-5.5", effort="max", harness="codex"),
|
|
122
|
+
Seat(model="claude-sonnet-5", effort="max", harness="claude", lens="adversarial"),
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
STANDIN_BOARDS: dict[str, Board] = {
|
|
127
|
+
DEFAULT_BOARD.name: DEFAULT_BOARD,
|
|
128
|
+
_STANDIN_CODE_REVIEW.name: _STANDIN_CODE_REVIEW,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# --- resolver ---------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class BoardResolver:
|
|
136
|
+
"""Resolve a board reference to seats against an (injectable) catalog + matrix.
|
|
137
|
+
|
|
138
|
+
``boards`` the named-board catalog; defaults to ``STANDIN_BOARDS``
|
|
139
|
+
(ABDREG injects the real config-loaded presets).
|
|
140
|
+
``default_board`` the board a bare ``advisor-board`` resolves to (settable
|
|
141
|
+
via ``set_default``).
|
|
142
|
+
``matrix`` frozen ``CompatibilityMatrix`` used to resolve a bare
|
|
143
|
+
ad-hoc seat's default lane; optional.
|
|
144
|
+
``aliases`` board-name aliases (``advisor-panel`` -> ``default`` by
|
|
145
|
+
default) so the historical name keeps resolving.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
boards: Mapping[str, Board] | None = None,
|
|
151
|
+
*,
|
|
152
|
+
default_board: str = "default",
|
|
153
|
+
matrix: CompatibilityMatrix | None = None,
|
|
154
|
+
aliases: Mapping[str, str] | None = None,
|
|
155
|
+
) -> None:
|
|
156
|
+
self._boards: dict[str, Board] = dict(boards) if boards is not None else dict(STANDIN_BOARDS)
|
|
157
|
+
self._aliases: dict[str, str] = dict(aliases) if aliases is not None else dict(BOARD_ALIASES)
|
|
158
|
+
self._matrix = matrix
|
|
159
|
+
self.set_default(default_board)
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def default_board(self) -> str:
|
|
163
|
+
return self._default_board
|
|
164
|
+
|
|
165
|
+
def set_default(self, name: str) -> None:
|
|
166
|
+
"""Set the board a bare ``advisor-board`` resolves to. Fails fast if the
|
|
167
|
+
name (after alias) is not in the catalog."""
|
|
168
|
+
resolved = self._resolve_name(name)
|
|
169
|
+
if resolved not in self._boards:
|
|
170
|
+
raise BoardResolutionError(name, self.available())
|
|
171
|
+
self._default_board = resolved
|
|
172
|
+
|
|
173
|
+
def available(self) -> tuple[str, ...]:
|
|
174
|
+
"""Every resolvable board name (catalog names + aliases)."""
|
|
175
|
+
return tuple(self._boards) + tuple(self._aliases)
|
|
176
|
+
|
|
177
|
+
def _resolve_name(self, name: str) -> str:
|
|
178
|
+
key = (name or "").strip()
|
|
179
|
+
return self._aliases.get(key, key)
|
|
180
|
+
|
|
181
|
+
def resolve(
|
|
182
|
+
self,
|
|
183
|
+
name: str | None = None,
|
|
184
|
+
*,
|
|
185
|
+
seats: str | Iterable[str] | None = None,
|
|
186
|
+
) -> Board:
|
|
187
|
+
"""Resolve to a concrete ``Board``.
|
|
188
|
+
|
|
189
|
+
* ``seats`` given -> a synthesized ad-hoc board (``--seats`` path);
|
|
190
|
+
* ``name`` is None/empty -> the settable default board;
|
|
191
|
+
* else -> the named board (alias-aware), or ``BoardResolutionError``.
|
|
192
|
+
"""
|
|
193
|
+
if seats is not None:
|
|
194
|
+
parsed = parse_seats(seats, matrix=self._matrix)
|
|
195
|
+
return Board(name="ad-hoc", purpose="ad-hoc", seats=parsed)
|
|
196
|
+
if name is None or not str(name).strip():
|
|
197
|
+
return self._boards[self._default_board]
|
|
198
|
+
resolved = self._resolve_name(name)
|
|
199
|
+
try:
|
|
200
|
+
return self._boards[resolved]
|
|
201
|
+
except KeyError:
|
|
202
|
+
raise BoardResolutionError(name, self.available()) from None
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def resolve_board(
|
|
206
|
+
name: str | None = None,
|
|
207
|
+
*,
|
|
208
|
+
seats: str | Iterable[str] | None = None,
|
|
209
|
+
boards: Mapping[str, Board] | None = None,
|
|
210
|
+
matrix: CompatibilityMatrix | None = None,
|
|
211
|
+
) -> Board:
|
|
212
|
+
"""One-shot convenience over ``BoardResolver`` (stand-in catalog by default)."""
|
|
213
|
+
return BoardResolver(boards, matrix=matrix).resolve(name, seats=seats)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# --- leg -> seat result re-keying (expressibility for same-vendor seats) -----
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def seat_result_key(seat: Seat) -> str:
|
|
220
|
+
"""The stable per-seat label a result is keyed by (``Seat.seat_key``)."""
|
|
221
|
+
return seat.seat_key
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def key_results_by_seat(
|
|
225
|
+
seats: Sequence[Seat], results: Sequence["object"]
|
|
226
|
+
) -> tuple[tuple[Seat, object], ...]:
|
|
227
|
+
"""Pair a board's seats with its panel-leg results **by position**.
|
|
228
|
+
|
|
229
|
+
The v4 ``PanelLegResult.leg`` keys results by vendor, so a board with two
|
|
230
|
+
same-vendor seats (e.g. two openai seats on ``codex`` and ``opencode``) could
|
|
231
|
+
not be told apart. Pairing by position, with ``seat.seat_key`` as the label,
|
|
232
|
+
makes two same-vendor seats distinctly expressible. ABDHOME wires the actual
|
|
233
|
+
per-seat spawn; ABDRESOLVE freezes the position<->seat_key identity.
|
|
234
|
+
"""
|
|
235
|
+
if len(seats) != len(results):
|
|
236
|
+
raise ValueError(
|
|
237
|
+
f"seat/result count mismatch: {len(seats)} seats vs {len(results)} results"
|
|
238
|
+
)
|
|
239
|
+
return tuple((seat, result) for seat, result in zip(seats, results))
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
__all__ = [
|
|
243
|
+
"SeatSpecError",
|
|
244
|
+
"BoardResolutionError",
|
|
245
|
+
"BOARD_ALIASES",
|
|
246
|
+
"STANDIN_BOARDS",
|
|
247
|
+
"BoardResolver",
|
|
248
|
+
"resolve_board",
|
|
249
|
+
"parse_seat_spec",
|
|
250
|
+
"parse_seats",
|
|
251
|
+
"seat_result_key",
|
|
252
|
+
"key_results_by_seat",
|
|
253
|
+
]
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""Advisor Board — frozen seat/board schema + vendor projection (IF-0-ABDFREEZE-1, -4).
|
|
2
|
+
|
|
3
|
+
This module is the **interface freeze** for the model-first Advisor Board. It is
|
|
4
|
+
additive and behavior-neutral: it introduces no runtime path and does not touch
|
|
5
|
+
`panel_invoker`. Downstream phases (ABDREG / ABDRESOLVE / ABDHOME) code against
|
|
6
|
+
the typed structures frozen here so the fan-out integrates without a big-bang.
|
|
7
|
+
|
|
8
|
+
The seat model inverts the v4 panel: a **seat is a cognition** — `{model, effort,
|
|
9
|
+
harness?, lens?, auth?}` — where the harness is a defaulted-but-overridable
|
|
10
|
+
execution *lane*, not the primary key. A **board** is a named, purpose-tagged,
|
|
11
|
+
open-ended list of seats.
|
|
12
|
+
|
|
13
|
+
Two freezes here are load-bearing for back-compat and must reproduce today's
|
|
14
|
+
behavior exactly (proven by ``tests/test_advisor_board_schema.py`` and
|
|
15
|
+
``tests/test_advisor_board_backcompat.py``):
|
|
16
|
+
|
|
17
|
+
* **Seat -> vendor-family projection** (``vendor_family`` / ``seat_vendor_family``)
|
|
18
|
+
must be byte-consistent with the existing governed-gate logic
|
|
19
|
+
(`governed_review.author_vendor_for_model` :60-75 and
|
|
20
|
+
`governed_review._EXECUTOR_VENDOR` :47-53). The governed reviewer != author
|
|
21
|
+
disjointness keys on vendor identity; model-first breaks the ``leg == vendor``
|
|
22
|
+
assumption, so ABDHOME rewires `governed_review` / `governed_premerge` to
|
|
23
|
+
consume *this* canonical projection. If the projection drifts, custom boards
|
|
24
|
+
silently corrupt reviewer-disjointness.
|
|
25
|
+
|
|
26
|
+
* **Host-leg identity** — which seat is the native in-process host leg when the
|
|
27
|
+
board runs *inside* a harness (e.g. Claude Code -> native ``Agent``) versus the
|
|
28
|
+
homebrew subprocess legs. Today the standalone Python runner spawns every leg
|
|
29
|
+
as a subprocess (no host leg), so the additive ``Seat.host_leg`` marker
|
|
30
|
+
defaults ``False`` and ``identify_host_leg`` returns ``None`` for the default
|
|
31
|
+
board — today's behavior, untouched.
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
from dataclasses import dataclass
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# --- canonical vocabularies -------------------------------------------------
|
|
41
|
+
|
|
42
|
+
# Model-first, harness-agnostic effort ladder. Each harness translates a canonical
|
|
43
|
+
# level to its own invocation form (see ``harness_mapping``). Ordered low -> high.
|
|
44
|
+
EFFORT_LEVELS: tuple[str, ...] = ("low", "medium", "high", "max")
|
|
45
|
+
|
|
46
|
+
# Credential lane for a seat. Subscription is the default lane (Assumptions §);
|
|
47
|
+
# ``api_key`` is only reachable behind an explicit board-level opt-in
|
|
48
|
+
# (``Board.allow_api_key_fallback``) and injects ONLY the seat vendor's key.
|
|
49
|
+
AUTH_SUBSCRIPTION = "subscription"
|
|
50
|
+
AUTH_API_KEY = "api_key"
|
|
51
|
+
AUTH_LANES: tuple[str, ...] = (AUTH_SUBSCRIPTION, AUTH_API_KEY)
|
|
52
|
+
|
|
53
|
+
# Provider transport backing a seat. ``homebrew`` keeps the built-3 + the native
|
|
54
|
+
# host leg (ABDHOME); ``omnigent`` supplies breadth via omniagent-plus -> Omnigent
|
|
55
|
+
# (ABDOMNI). Default homebrew keeps the default board all-homebrew.
|
|
56
|
+
BACKING_HOMEBREW = "homebrew"
|
|
57
|
+
BACKING_OMNIGENT = "omnigent"
|
|
58
|
+
PROVIDER_BACKINGS: tuple[str, ...] = (BACKING_HOMEBREW, BACKING_OMNIGENT)
|
|
59
|
+
|
|
60
|
+
# Board config format + location (IF-0-ABDFREEZE-1). Honors ``XDG_CONFIG_HOME``.
|
|
61
|
+
CONFIG_RELATIVE_PATH = "agent-harness/advisor-boards.toml"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def board_config_path(env: "os._Environ[str] | dict[str, str] | None" = None) -> Path:
|
|
65
|
+
"""Frozen config location: ``$XDG_CONFIG_HOME/agent-harness/advisor-boards.toml``
|
|
66
|
+
(default ``~/.config/agent-harness/advisor-boards.toml``). The loader lands in
|
|
67
|
+
ABDREG; this only freezes *where* it reads."""
|
|
68
|
+
src = os.environ if env is None else env
|
|
69
|
+
base = src.get("XDG_CONFIG_HOME") or str(Path.home() / ".config")
|
|
70
|
+
return Path(base) / CONFIG_RELATIVE_PATH
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# --- vendor-family projection (governed reviewer != author, model-first) -----
|
|
74
|
+
|
|
75
|
+
# Harness -> vendor family. Mirrors `governed_review._EXECUTOR_VENDOR` (:47-53)
|
|
76
|
+
# EXACTLY so the projection reproduces today's executor->vendor mapping. Two
|
|
77
|
+
# same-vendor seats on different harnesses (gpt-5.5 on ``codex`` and on
|
|
78
|
+
# ``opencode``) MUST project to the same family, which this table encodes.
|
|
79
|
+
_HARNESS_VENDOR: dict[str, str] = {
|
|
80
|
+
"codex": "codex",
|
|
81
|
+
"opencode": "codex", # openai-family models
|
|
82
|
+
"claude": "claude",
|
|
83
|
+
"gemini": "gemini",
|
|
84
|
+
"pi": "pi",
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def vendor_of_harness(harness: str) -> str:
|
|
89
|
+
"""Harness (execution lane) -> vendor family, with an identity fallback for an
|
|
90
|
+
unknown harness. Byte-consistent with
|
|
91
|
+
`governed_review.author_vendor_for_executor`."""
|
|
92
|
+
h = (harness or "").lower()
|
|
93
|
+
return _HARNESS_VENDOR.get(h, h)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def vendor_of_model(model: str) -> str:
|
|
97
|
+
"""Concrete model id -> vendor family, or ``""`` when the model is inconclusive.
|
|
98
|
+
|
|
99
|
+
The matched branches reproduce `governed_review.author_vendor_for_model`
|
|
100
|
+
(:69-74) EXACTLY. The one intentional difference: this returns ``""`` (rather
|
|
101
|
+
than the raw model) for an unmatched model, so ``vendor_family`` can then fall
|
|
102
|
+
back to the harness lane. ``author_vendor_for_model``-equivalence for a bare
|
|
103
|
+
model is recovered as ``vendor_of_model(m) or m.lower()`` and is asserted in
|
|
104
|
+
the schema test.
|
|
105
|
+
"""
|
|
106
|
+
m = (model or "").lower()
|
|
107
|
+
if not m:
|
|
108
|
+
return ""
|
|
109
|
+
if "claude" in m or "opus" in m or "sonnet" in m or "haiku" in m:
|
|
110
|
+
return "claude"
|
|
111
|
+
if "gemini" in m or m in {"pro", "flash", "flash-lite", "auto"}:
|
|
112
|
+
return "gemini"
|
|
113
|
+
if m.startswith("gpt") or m.startswith("o1") or m.startswith("o3") or m.startswith("openai/"):
|
|
114
|
+
return "codex" # the codex panel leg runs the openai-family model
|
|
115
|
+
return ""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def vendor_family(model: str, harness: str | None = None) -> str:
|
|
119
|
+
"""Canonical model-first seat -> vendor-family projection.
|
|
120
|
+
|
|
121
|
+
Model wins when conclusive; otherwise the harness lane decides; otherwise the
|
|
122
|
+
lowercased model is the family (matching the historical bare-model fallback).
|
|
123
|
+
This is THE canonical projection ABDHOME rewires the governed gates onto — not
|
|
124
|
+
a parallel copy — so reviewer != author disjointness survives model-first.
|
|
125
|
+
"""
|
|
126
|
+
fam = vendor_of_model(model)
|
|
127
|
+
if fam:
|
|
128
|
+
return fam
|
|
129
|
+
if harness:
|
|
130
|
+
return vendor_of_harness(harness)
|
|
131
|
+
return (model or "").lower()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# --- seat + board -----------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass(frozen=True)
|
|
138
|
+
class Seat:
|
|
139
|
+
"""A single cognition on a board (model-first).
|
|
140
|
+
|
|
141
|
+
``model`` concrete model id (the primary key). Required.
|
|
142
|
+
``effort`` canonical effort level (``EFFORT_LEVELS``). Required — the
|
|
143
|
+
model-registry default is applied by the resolver (ABDRESOLVE),
|
|
144
|
+
not silently here.
|
|
145
|
+
``harness`` execution lane; ``None`` -> ``default_lane(model)`` at resolution.
|
|
146
|
+
``lens`` optional review persona/lens (advisory framing); no behavior yet.
|
|
147
|
+
``auth`` credential lane (``AUTH_LANES``); subscription by default.
|
|
148
|
+
``backing`` provider transport (``PROVIDER_BACKINGS``); homebrew by default.
|
|
149
|
+
``host_leg`` additive marker: this seat is the native in-process host leg
|
|
150
|
+
(not a subprocess). Default ``False`` == today's subprocess leg.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
model: str
|
|
154
|
+
effort: str
|
|
155
|
+
harness: str | None = None
|
|
156
|
+
lens: str | None = None
|
|
157
|
+
auth: str = AUTH_SUBSCRIPTION
|
|
158
|
+
backing: str = BACKING_HOMEBREW
|
|
159
|
+
host_leg: bool = False
|
|
160
|
+
|
|
161
|
+
def __post_init__(self) -> None:
|
|
162
|
+
if not self.model or not str(self.model).strip():
|
|
163
|
+
raise ValueError("seat.model is required")
|
|
164
|
+
if self.effort not in EFFORT_LEVELS:
|
|
165
|
+
raise ValueError(f"seat.effort {self.effort!r} not in {EFFORT_LEVELS}")
|
|
166
|
+
if self.auth not in AUTH_LANES:
|
|
167
|
+
raise ValueError(f"seat.auth {self.auth!r} not in {AUTH_LANES}")
|
|
168
|
+
if self.backing not in PROVIDER_BACKINGS:
|
|
169
|
+
raise ValueError(f"seat.backing {self.backing!r} not in {PROVIDER_BACKINGS}")
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def vendor_family(self) -> str:
|
|
173
|
+
"""This seat's vendor family (model-first projection)."""
|
|
174
|
+
return vendor_family(self.model, self.harness)
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def seat_key(self) -> str:
|
|
178
|
+
"""Stable per-seat LABEL for result re-keying (ABDRESOLVE re-keys
|
|
179
|
+
``PanelLegResult.leg`` -> seat so two same-vendor seats are expressible).
|
|
180
|
+
|
|
181
|
+
Incorporates every user-distinguishing field — (harness-lane, model,
|
|
182
|
+
effort, lens) — so seats that differ only by ``lens`` (a natural
|
|
183
|
+
brainstorm board: same model, adversarial vs supportive) get distinct
|
|
184
|
+
keys. This is a LABEL, not a guaranteed-unique id: a board may still hold
|
|
185
|
+
two byte-identical seats, so ABDRESOLVE MUST key results by **seat
|
|
186
|
+
position** and use ``seat_key`` only as the human-readable label. Frozen
|
|
187
|
+
this way so the fan-out builds on a stable, collision-aware contract.
|
|
188
|
+
"""
|
|
189
|
+
lane = self.harness or f"@{vendor_family(self.model)}"
|
|
190
|
+
base = f"{lane}:{self.model}:{self.effort}"
|
|
191
|
+
return f"{base}:{self.lens}" if self.lens else base
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@dataclass(frozen=True)
|
|
195
|
+
class Board:
|
|
196
|
+
"""A named, purpose-tagged, open-ended list of seats.
|
|
197
|
+
|
|
198
|
+
``allow_api_key_fallback`` defaults ``False`` — no seat may use the api-key
|
|
199
|
+
lane unless the board explicitly opts in (never-silent-key, IF-0-ABDFREEZE-3).
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
name: str
|
|
203
|
+
purpose: str
|
|
204
|
+
seats: tuple[Seat, ...]
|
|
205
|
+
allow_api_key_fallback: bool = False
|
|
206
|
+
|
|
207
|
+
def __post_init__(self) -> None:
|
|
208
|
+
if not self.name or not str(self.name).strip():
|
|
209
|
+
raise ValueError("board.name is required")
|
|
210
|
+
if not isinstance(self.seats, tuple):
|
|
211
|
+
# keep the board hashable/frozen: seats must be a tuple of Seat
|
|
212
|
+
raise TypeError("board.seats must be a tuple of Seat")
|
|
213
|
+
for seat in self.seats:
|
|
214
|
+
if not isinstance(seat, Seat):
|
|
215
|
+
raise TypeError(f"board.seats entries must be Seat, got {type(seat)!r}")
|
|
216
|
+
if not self.allow_api_key_fallback:
|
|
217
|
+
offenders = [s for s in self.seats if s.auth == AUTH_API_KEY]
|
|
218
|
+
if offenders:
|
|
219
|
+
raise ValueError(
|
|
220
|
+
"board has api_key seats but allow_api_key_fallback is False "
|
|
221
|
+
"(never-silent-key): "
|
|
222
|
+
+ ", ".join(s.seat_key for s in offenders)
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def seat_vendor_families(self) -> tuple[str, ...]:
|
|
226
|
+
return tuple(seat.vendor_family for seat in self.seats)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def seat_vendor_family(seat: Seat) -> str:
|
|
230
|
+
"""Free-function form of ``Seat.vendor_family`` (the projection ABDHOME calls
|
|
231
|
+
when it holds a seat but not the property)."""
|
|
232
|
+
return vendor_family(seat.model, seat.harness)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# --- host-leg identity ------------------------------------------------------
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@dataclass(frozen=True)
|
|
239
|
+
class HostContext:
|
|
240
|
+
"""The runtime host the board is currently running *inside*.
|
|
241
|
+
|
|
242
|
+
``host_harness`` is the harness whose process is hosting the board (e.g.
|
|
243
|
+
``"claude"`` when the board runs inside Claude Code and can reach the native
|
|
244
|
+
in-process ``Agent`` tool). ``None`` == the standalone Python runner, which
|
|
245
|
+
spawns every leg as a subprocess — TODAY'S DEFAULT, no host leg.
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
host_harness: str | None = None
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def identify_host_leg(board: Board, host: HostContext | None = None) -> Seat | None:
|
|
252
|
+
"""Return the seat that IS the native host leg for ``host``, or ``None``.
|
|
253
|
+
|
|
254
|
+
A seat is the host leg iff (a) the board runs inside a harness
|
|
255
|
+
(``host.host_harness`` set), and (b) the seat's resolved lane matches that
|
|
256
|
+
harness, and (c) the seat is marked ``host_leg`` OR it is the first seat on
|
|
257
|
+
the hosting harness (auto-identification for a board that predates the
|
|
258
|
+
marker). For the standalone runner (``host_harness is None``) this returns
|
|
259
|
+
``None`` — every leg is a subprocess, exactly as today. The native host leg,
|
|
260
|
+
once identified, must never be routed through the gateway (ABDHOME invariant).
|
|
261
|
+
"""
|
|
262
|
+
if host is None or not host.host_harness:
|
|
263
|
+
return None
|
|
264
|
+
hosting = host.host_harness.lower()
|
|
265
|
+
on_host = [s for s in board.seats if (s.harness or "").lower() == hosting]
|
|
266
|
+
if not on_host:
|
|
267
|
+
return None
|
|
268
|
+
explicit = [s for s in on_host if s.host_leg]
|
|
269
|
+
return explicit[0] if explicit else on_host[0]
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Fixture-backed stand-in registries + matrix (ABDRESOLVE / ABDHOME test support).
|
|
2
|
+
|
|
3
|
+
ABDREG (Phase 2) populates the *real* six-harness registry, model registry, and
|
|
4
|
+
``(model x harness)`` compatibility matrix behind the frozen Protocols in
|
|
5
|
+
``registries``. Until then, ABDRESOLVE codes against those Protocols using a
|
|
6
|
+
stand-in built **from the shared canonical fixtures** (``fixtures.py``) — the
|
|
7
|
+
ABDFREEZE anti-divergence keystone. Because the stand-in is derived from the same
|
|
8
|
+
``CANONICAL_VALID_PAIRS`` / ``CANONICAL_INVALID_PAIRS`` / ``DEFAULT_SEATS`` that
|
|
9
|
+
ABDREG populates from, the resolver/validation logic verified here integrates with
|
|
10
|
+
ABDREG's real data at ABDVERIFY without a mock-vs-real cliff.
|
|
11
|
+
|
|
12
|
+
This module is deliberately thin and additive: it introduces no running path and
|
|
13
|
+
is imported only by the resolver's default catalog and by tests. ABDREG replaces
|
|
14
|
+
these with populated implementations; ABDRESOLVE never depends on ABDREG's live
|
|
15
|
+
output (integration is at ABDVERIFY).
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
|
|
21
|
+
from .fixtures import (
|
|
22
|
+
CANONICAL_INVALID_PAIRS,
|
|
23
|
+
CANONICAL_VALID_PAIRS,
|
|
24
|
+
DEFAULT_SEATS,
|
|
25
|
+
)
|
|
26
|
+
from .registries import (
|
|
27
|
+
AuthAvailability,
|
|
28
|
+
HarnessSpec,
|
|
29
|
+
MatrixVerdict,
|
|
30
|
+
ModelSpec,
|
|
31
|
+
)
|
|
32
|
+
from .schema import (
|
|
33
|
+
AUTH_SUBSCRIPTION,
|
|
34
|
+
BACKING_HOMEBREW,
|
|
35
|
+
vendor_family,
|
|
36
|
+
vendor_of_harness,
|
|
37
|
+
vendor_of_model,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# The default lane a bare ``model`` seat resolves onto, derived from the canonical
|
|
42
|
+
# default seats (the built-3) plus the same-vendor-across-harness valid pairs. This
|
|
43
|
+
# is the stand-in for ``ModelRegistry.default_lane`` / ``CompatibilityMatrix.default_lane``.
|
|
44
|
+
_DEFAULT_LANE: dict[str, str] = {seat.model: seat.harness or "" for seat in DEFAULT_SEATS}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _default_lane_for(model: str) -> str:
|
|
48
|
+
lane = _DEFAULT_LANE.get(model)
|
|
49
|
+
if lane:
|
|
50
|
+
return lane
|
|
51
|
+
# Fall back to the first canonical valid lane for the model, else the vendor family.
|
|
52
|
+
for pair_model, pair_harness in CANONICAL_VALID_PAIRS:
|
|
53
|
+
if pair_model == model:
|
|
54
|
+
return pair_harness
|
|
55
|
+
return vendor_family(model)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class StandinCompatibilityMatrix:
|
|
60
|
+
"""A ``CompatibilityMatrix`` (frozen Protocol) backed by the canonical fixtures.
|
|
61
|
+
|
|
62
|
+
``is_valid`` accepts every ``CANONICAL_VALID_PAIRS`` entry (and any
|
|
63
|
+
same-vendor-family pairing derivable from them), rejects every
|
|
64
|
+
``CANONICAL_INVALID_PAIRS`` entry, and rejects an unknown pairing as a
|
|
65
|
+
cross-vendor mismatch. The returned ``AuthAvailability`` reports the
|
|
66
|
+
subscription lane available for a valid pair (the default lane) so a
|
|
67
|
+
valid-but-unauthed seat is *expressible* distinctly from an invalid pairing.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def is_valid(self, model: str, harness: str) -> MatrixVerdict:
|
|
71
|
+
pair = (model, harness)
|
|
72
|
+
if pair in CANONICAL_INVALID_PAIRS:
|
|
73
|
+
return (False, AuthAvailability(detail=f"{model} does not run on the {harness} lane"))
|
|
74
|
+
if pair in CANONICAL_VALID_PAIRS:
|
|
75
|
+
return (True, AuthAvailability(subscription=True, detail="subscription"))
|
|
76
|
+
# Not enumerated: valid iff the model's vendor family matches the harness
|
|
77
|
+
# lane's family (the same rule the canonical pairs encode) — so gpt-5.5 on
|
|
78
|
+
# any openai-family lane is valid, gpt-5.5 on claude is not.
|
|
79
|
+
model_fam = vendor_of_model(model)
|
|
80
|
+
lane_fam = vendor_of_harness(harness)
|
|
81
|
+
if model_fam and model_fam == lane_fam:
|
|
82
|
+
return (True, AuthAvailability(subscription=True, detail="subscription"))
|
|
83
|
+
return (
|
|
84
|
+
False,
|
|
85
|
+
AuthAvailability(detail=f"{model} is not compatible with the {harness} lane"),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def default_lane(self, model: str) -> str:
|
|
89
|
+
return _default_lane_for(model)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass(frozen=True)
|
|
93
|
+
class StandinModelRegistry:
|
|
94
|
+
"""A ``ModelRegistry`` (frozen Protocol) backed by the canonical fixtures.
|
|
95
|
+
|
|
96
|
+
``runnable_by`` lists every canonical lane a model is valid on — used by
|
|
97
|
+
``validation`` to render an actionable "did you mean" diagnostic on an invalid
|
|
98
|
+
seat.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
def list_models(self) -> tuple[ModelSpec, ...]:
|
|
102
|
+
return tuple(self.get(model) for model in _canonical_models())
|
|
103
|
+
|
|
104
|
+
def get(self, model: str) -> ModelSpec:
|
|
105
|
+
lanes = runnable_lanes(model)
|
|
106
|
+
return ModelSpec(
|
|
107
|
+
model=model,
|
|
108
|
+
vendor_family=vendor_family(model),
|
|
109
|
+
default_lane=_default_lane_for(model),
|
|
110
|
+
runnable_by=lanes,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def default_lane(self, model: str) -> str:
|
|
114
|
+
return _default_lane_for(model)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass(frozen=True)
|
|
118
|
+
class StandinHarnessRegistry:
|
|
119
|
+
"""A minimal ``HarnessRegistry`` over the canonical lanes (probe-agnostic)."""
|
|
120
|
+
|
|
121
|
+
def list_harnesses(self) -> tuple[HarnessSpec, ...]:
|
|
122
|
+
return tuple(self.get(name) for name in _canonical_harnesses())
|
|
123
|
+
|
|
124
|
+
def get(self, name: str) -> HarnessSpec:
|
|
125
|
+
return HarnessSpec(
|
|
126
|
+
name=name,
|
|
127
|
+
cli=name,
|
|
128
|
+
auth_lanes=(AUTH_SUBSCRIPTION,),
|
|
129
|
+
backing=BACKING_HOMEBREW,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def is_available(self, name: str) -> bool:
|
|
133
|
+
return name in _canonical_harnesses()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def runnable_lanes(model: str) -> tuple[str, ...]:
|
|
137
|
+
"""The canonical harness lanes a ``model`` is valid on (fixture-derived)."""
|
|
138
|
+
lanes: list[str] = []
|
|
139
|
+
for pair_model, pair_harness in CANONICAL_VALID_PAIRS:
|
|
140
|
+
if pair_model == model and pair_harness not in lanes:
|
|
141
|
+
lanes.append(pair_harness)
|
|
142
|
+
return tuple(lanes)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _canonical_models() -> tuple[str, ...]:
|
|
146
|
+
seen: list[str] = []
|
|
147
|
+
for model, _ in CANONICAL_VALID_PAIRS:
|
|
148
|
+
if model not in seen:
|
|
149
|
+
seen.append(model)
|
|
150
|
+
return tuple(seen)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _canonical_harnesses() -> tuple[str, ...]:
|
|
154
|
+
seen: list[str] = []
|
|
155
|
+
for _, harness in CANONICAL_VALID_PAIRS:
|
|
156
|
+
if harness not in seen:
|
|
157
|
+
seen.append(harness)
|
|
158
|
+
return tuple(seen)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
__all__ = [
|
|
162
|
+
"StandinCompatibilityMatrix",
|
|
163
|
+
"StandinModelRegistry",
|
|
164
|
+
"StandinHarnessRegistry",
|
|
165
|
+
"runnable_lanes",
|
|
166
|
+
]
|