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,225 @@
|
|
|
1
|
+
"""User-editable board config loader (ABDREG, Phase 2 — lane 4).
|
|
2
|
+
|
|
3
|
+
Loads ``$XDG_CONFIG_HOME/agent-harness/advisor-boards.toml`` (the FROZEN location,
|
|
4
|
+
``schema.board_config_path``; format frozen by
|
|
5
|
+
``fixtures/advisor-boards.example.toml``) and layers user boards on top of the
|
|
6
|
+
built-in ``presets``. Contract:
|
|
7
|
+
|
|
8
|
+
* The four built-in presets are always present (base layer); a user ``[[boards]]``
|
|
9
|
+
with the same ``name`` overrides its preset.
|
|
10
|
+
* ``allow_api_key_fallback`` defaults ``False`` — a board with an ``api_key`` seat
|
|
11
|
+
and no opt-in is rejected by the ``Board`` schema (never-silent-key).
|
|
12
|
+
* **Unknown keys are a hard error, never a silent drop** — an unrecognised
|
|
13
|
+
top-level, board, or seat key raises ``BoardConfigError`` naming the key.
|
|
14
|
+
* Every board (presets AND user boards) is validated through the compatibility
|
|
15
|
+
matrix at load time, so an invalid ``(model, harness)`` pairing (e.g.
|
|
16
|
+
``claude:gpt-5.5``) or an over-ceiling effort is rejected at CONFIG TIME with an
|
|
17
|
+
actionable message (``validation.validate_board``).
|
|
18
|
+
|
|
19
|
+
A missing config file is not an error: the built-in presets load on their own.
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from collections.abc import Mapping
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
try: # Python 3.11+
|
|
29
|
+
import tomllib # type: ignore[import-not-found]
|
|
30
|
+
except ModuleNotFoundError: # Python 3.10 — the requires-python floor
|
|
31
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
32
|
+
|
|
33
|
+
from .validation import SeatValidationError, validate_board
|
|
34
|
+
from .presets import DEFAULT_BOARD_NAME, PRESETS
|
|
35
|
+
from .schema import (
|
|
36
|
+
AUTH_LANES,
|
|
37
|
+
PROVIDER_BACKINGS,
|
|
38
|
+
Board,
|
|
39
|
+
Seat,
|
|
40
|
+
board_config_path,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Recognised keys — anything else is a hard error (no silent drop).
|
|
44
|
+
_KNOWN_TOP_KEYS: frozenset[str] = frozenset({"default_board", "boards"})
|
|
45
|
+
_KNOWN_BOARD_KEYS: frozenset[str] = frozenset(
|
|
46
|
+
{"name", "purpose", "allow_api_key_fallback", "seats"}
|
|
47
|
+
)
|
|
48
|
+
_KNOWN_SEAT_KEYS: frozenset[str] = frozenset(
|
|
49
|
+
{"model", "effort", "harness", "lens", "auth", "backing", "host_leg"}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class BoardConfigError(ValueError):
|
|
54
|
+
"""The board config is malformed: an unknown key, a wrong-typed value, a
|
|
55
|
+
missing required field, or a board that fails matrix validation."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass(frozen=True)
|
|
59
|
+
class BoardConfig:
|
|
60
|
+
"""Resolved board set: the built-in presets overlaid with the user's boards,
|
|
61
|
+
plus the resolved ``default_board`` name."""
|
|
62
|
+
|
|
63
|
+
boards: dict[str, Board]
|
|
64
|
+
default_board: str
|
|
65
|
+
|
|
66
|
+
def get(self, name: str | None = None) -> Board:
|
|
67
|
+
"""Resolve a board by name; a bare ``advisor-board`` (``name is None``)
|
|
68
|
+
resolves to ``default_board``. Raises ``BoardConfigError`` naming the
|
|
69
|
+
available boards for an unknown name."""
|
|
70
|
+
key = name or self.default_board
|
|
71
|
+
try:
|
|
72
|
+
return self.boards[key]
|
|
73
|
+
except KeyError as exc:
|
|
74
|
+
available = ", ".join(sorted(self.boards))
|
|
75
|
+
raise BoardConfigError(
|
|
76
|
+
f"unknown board {key!r}; available boards: {available}"
|
|
77
|
+
) from exc
|
|
78
|
+
|
|
79
|
+
def names(self) -> tuple[str, ...]:
|
|
80
|
+
return tuple(sorted(self.boards))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _reject_unknown(keys, allowed: frozenset[str], where: str) -> None:
|
|
84
|
+
unknown = sorted(set(keys) - allowed)
|
|
85
|
+
if unknown:
|
|
86
|
+
raise BoardConfigError(
|
|
87
|
+
f"unknown config key(s) {unknown} in {where}; "
|
|
88
|
+
f"recognised keys: {sorted(allowed)}"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _require_bool(raw: Mapping[str, Any], key: str, default: bool, where: str) -> bool:
|
|
93
|
+
"""Read a boolean config key STRICTLY: an absent key yields ``default``, a
|
|
94
|
+
present key MUST be a literal TOML boolean. Never coerce a non-bool scalar —
|
|
95
|
+
``bool("false")`` is ``True``, so coercion would silently flip an opt-in gate
|
|
96
|
+
(e.g. a quoted ``allow_api_key_fallback = "false"`` would enable the api-key
|
|
97
|
+
fallback). A wrong-typed value is a hard ``BoardConfigError`` naming the key."""
|
|
98
|
+
if key not in raw:
|
|
99
|
+
return default
|
|
100
|
+
value = raw[key]
|
|
101
|
+
if not isinstance(value, bool):
|
|
102
|
+
raise BoardConfigError(
|
|
103
|
+
f"{where}: {key!r} must be a boolean (true/false), got "
|
|
104
|
+
f"{type(value).__name__} {value!r}"
|
|
105
|
+
)
|
|
106
|
+
return value
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _parse_seat(raw: Mapping[str, Any], where: str) -> Seat:
|
|
110
|
+
if not isinstance(raw, Mapping):
|
|
111
|
+
raise BoardConfigError(f"{where} must be a table, got {type(raw).__name__}")
|
|
112
|
+
_reject_unknown(raw.keys(), _KNOWN_SEAT_KEYS, where)
|
|
113
|
+
if "model" not in raw:
|
|
114
|
+
raise BoardConfigError(f"{where} is missing the required 'model' key")
|
|
115
|
+
if "effort" not in raw:
|
|
116
|
+
raise BoardConfigError(f"{where} (model={raw['model']!r}) is missing the required 'effort' key")
|
|
117
|
+
auth = raw.get("auth", AUTH_LANES[0])
|
|
118
|
+
backing = raw.get("backing", PROVIDER_BACKINGS[0])
|
|
119
|
+
try:
|
|
120
|
+
return Seat(
|
|
121
|
+
model=str(raw["model"]),
|
|
122
|
+
effort=str(raw["effort"]),
|
|
123
|
+
harness=(str(raw["harness"]) if raw.get("harness") is not None else None),
|
|
124
|
+
lens=(str(raw["lens"]) if raw.get("lens") is not None else None),
|
|
125
|
+
auth=str(auth),
|
|
126
|
+
backing=str(backing),
|
|
127
|
+
host_leg=_require_bool(raw, "host_leg", False, where),
|
|
128
|
+
)
|
|
129
|
+
except (ValueError, TypeError) as exc:
|
|
130
|
+
# Seat.__post_init__ fail-closed validation (bad effort/auth/backing) ->
|
|
131
|
+
# surface as a config error at load time.
|
|
132
|
+
raise BoardConfigError(f"{where}: {exc}") from exc
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _parse_board(raw: Mapping[str, Any], index: int) -> Board:
|
|
136
|
+
where = f"boards[{index}]"
|
|
137
|
+
if not isinstance(raw, Mapping):
|
|
138
|
+
raise BoardConfigError(f"{where} must be a table, got {type(raw).__name__}")
|
|
139
|
+
_reject_unknown(raw.keys(), _KNOWN_BOARD_KEYS, where)
|
|
140
|
+
if "name" not in raw:
|
|
141
|
+
raise BoardConfigError(f"{where} is missing the required 'name' key")
|
|
142
|
+
name = str(raw["name"])
|
|
143
|
+
seats_raw = raw.get("seats", [])
|
|
144
|
+
if not isinstance(seats_raw, list):
|
|
145
|
+
raise BoardConfigError(f"board {name!r} 'seats' must be a list of seat tables")
|
|
146
|
+
seats = tuple(
|
|
147
|
+
_parse_seat(seat, f"board {name!r} seats[{i}]") for i, seat in enumerate(seats_raw)
|
|
148
|
+
)
|
|
149
|
+
try:
|
|
150
|
+
return Board(
|
|
151
|
+
name=name,
|
|
152
|
+
purpose=str(raw.get("purpose", "")),
|
|
153
|
+
seats=seats,
|
|
154
|
+
allow_api_key_fallback=_require_bool(
|
|
155
|
+
raw, "allow_api_key_fallback", False, where
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
except (ValueError, TypeError) as exc:
|
|
159
|
+
# Board.__post_init__ (e.g. api_key seat without opt-in) -> config error.
|
|
160
|
+
raise BoardConfigError(f"board {name!r}: {exc}") from exc
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def load_boards(
|
|
164
|
+
path: Path | None = None,
|
|
165
|
+
*,
|
|
166
|
+
env: Mapping[str, str] | None = None,
|
|
167
|
+
matrix: Any | None = None,
|
|
168
|
+
validate: bool = True,
|
|
169
|
+
) -> BoardConfig:
|
|
170
|
+
"""Load the board config, layering user boards over the built-in presets.
|
|
171
|
+
|
|
172
|
+
``path`` defaults to ``board_config_path(env)``. A missing file yields just the
|
|
173
|
+
presets. ``matrix`` defaults to ``matrix.default_matrix()``; pass ``validate=
|
|
174
|
+
False`` only for shape-parsing without availability (kept for tests). Raises
|
|
175
|
+
``BoardConfigError`` for any unknown key, malformed value, or matrix-invalid
|
|
176
|
+
board — never a silent drop.
|
|
177
|
+
"""
|
|
178
|
+
boards: dict[str, Board] = dict(PRESETS)
|
|
179
|
+
default_board = DEFAULT_BOARD_NAME
|
|
180
|
+
|
|
181
|
+
cfg_path = path if path is not None else board_config_path(env)
|
|
182
|
+
if cfg_path.exists():
|
|
183
|
+
with open(cfg_path, "rb") as fh:
|
|
184
|
+
try:
|
|
185
|
+
data = tomllib.load(fh)
|
|
186
|
+
except tomllib.TOMLDecodeError as exc:
|
|
187
|
+
raise BoardConfigError(f"{cfg_path} is not valid TOML: {exc}") from exc
|
|
188
|
+
_reject_unknown(data.keys(), _KNOWN_TOP_KEYS, str(cfg_path))
|
|
189
|
+
raw_boards = data.get("boards", [])
|
|
190
|
+
if not isinstance(raw_boards, list):
|
|
191
|
+
raise BoardConfigError(f"{cfg_path}: 'boards' must be an array of tables")
|
|
192
|
+
for i, raw in enumerate(raw_boards):
|
|
193
|
+
board = _parse_board(raw, i)
|
|
194
|
+
boards[board.name] = board # user board overrides a same-named preset
|
|
195
|
+
if "default_board" in data:
|
|
196
|
+
default_board = str(data["default_board"])
|
|
197
|
+
|
|
198
|
+
if default_board not in boards:
|
|
199
|
+
available = ", ".join(sorted(boards))
|
|
200
|
+
raise BoardConfigError(
|
|
201
|
+
f"default_board {default_board!r} is not a defined board; "
|
|
202
|
+
f"available boards: {available}"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if validate:
|
|
206
|
+
if matrix is None:
|
|
207
|
+
from .matrix import default_matrix
|
|
208
|
+
|
|
209
|
+
matrix = default_matrix(env=env)
|
|
210
|
+
for board in boards.values():
|
|
211
|
+
try:
|
|
212
|
+
validate_board(board, matrix=matrix)
|
|
213
|
+
except SeatValidationError as exc:
|
|
214
|
+
# Surface matrix-level rejections under the config error type so a
|
|
215
|
+
# caller catches one exception for any load-time failure.
|
|
216
|
+
raise BoardConfigError(str(exc)) from exc
|
|
217
|
+
|
|
218
|
+
return BoardConfig(boards=boards, default_board=default_board)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
__all__ = [
|
|
222
|
+
"BoardConfig",
|
|
223
|
+
"BoardConfigError",
|
|
224
|
+
"load_boards",
|
|
225
|
+
]
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""Internal advisor-board event envelope + best-effort forwarding (IF-0-ABDFREEZE-5).
|
|
2
|
+
|
|
3
|
+
This is OUR event shape — deliberately NOT a guessed Omnigent schema. It freezes
|
|
4
|
+
the observability contract's two load-bearing rules:
|
|
5
|
+
|
|
6
|
+
* **launcher != observability-plane**: a natively-launched leg stays natively
|
|
7
|
+
launched and *emits* an ``AdvisorBoardEvent`` into a forwarded stream; it is
|
|
8
|
+
never relaunched through the gateway for observability's sake.
|
|
9
|
+
* **forwarding is async/best-effort and can NEVER delay or fail the native
|
|
10
|
+
leg**: ``best_effort_forward`` swallows every sink error and (by contract)
|
|
11
|
+
hands the event to the sink off the leg's critical path.
|
|
12
|
+
|
|
13
|
+
The MAPPING from this envelope to a concrete sink (an Omnigent v0.4.0 ingestion
|
|
14
|
+
endpoint, or omniagent-plus's own ui-read-model / state-ledger) is deferred to
|
|
15
|
+
ABDOBS — do NOT freeze against a guessed upstream schema.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from collections.abc import Mapping
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from typing import Any, Protocol, runtime_checkable
|
|
22
|
+
|
|
23
|
+
# Bumped only on a breaking change to the envelope; sinks key off it.
|
|
24
|
+
EVENT_SCHEMA_VERSION = "advisor_board.event.v1"
|
|
25
|
+
|
|
26
|
+
# The frozen event kinds a board leg emits. Board-level bracket the run; seat-level
|
|
27
|
+
# mirror a leg's lifecycle (aligned with the runtime ``runtime.*`` names in
|
|
28
|
+
# agent_runtime_provider.RuntimeEvent, but named in OUR namespace).
|
|
29
|
+
EVENT_KINDS: tuple[str, ...] = (
|
|
30
|
+
"board.started",
|
|
31
|
+
"board.completed",
|
|
32
|
+
"seat.started",
|
|
33
|
+
"seat.text.delta",
|
|
34
|
+
"seat.completed",
|
|
35
|
+
"seat.failed",
|
|
36
|
+
"seat.skipped",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class AdvisorBoardEvent:
|
|
42
|
+
"""One observability event from a board run — our envelope, sink-agnostic.
|
|
43
|
+
|
|
44
|
+
``board`` the board name (e.g. ``"default"``).
|
|
45
|
+
``seat_key`` the emitting seat's stable identity (``Seat.seat_key``);
|
|
46
|
+
empty for ``board.*`` events.
|
|
47
|
+
``vendor_family`` the seat's projected vendor family (empty for board events).
|
|
48
|
+
``harness`` the seat's execution lane (empty for board events).
|
|
49
|
+
``kind`` one of ``EVENT_KINDS``.
|
|
50
|
+
``sequence`` monotonic per board run.
|
|
51
|
+
``occurred_at`` ISO-8601 UTC timestamp (caller-supplied; frozen field only).
|
|
52
|
+
``payload`` kind-specific fields (e.g. ``{"delta": ...}`` /
|
|
53
|
+
``{"status": ...}``); never carries a raw API key.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
kind: str
|
|
57
|
+
board: str
|
|
58
|
+
sequence: int
|
|
59
|
+
occurred_at: str
|
|
60
|
+
seat_key: str = ""
|
|
61
|
+
vendor_family: str = ""
|
|
62
|
+
harness: str = ""
|
|
63
|
+
schema_version: str = EVENT_SCHEMA_VERSION
|
|
64
|
+
payload: Mapping[str, Any] = field(default_factory=dict)
|
|
65
|
+
|
|
66
|
+
def __post_init__(self) -> None:
|
|
67
|
+
if self.kind not in EVENT_KINDS:
|
|
68
|
+
raise ValueError(f"event.kind {self.kind!r} not in {EVENT_KINDS}")
|
|
69
|
+
|
|
70
|
+
def to_json(self) -> dict[str, Any]:
|
|
71
|
+
"""Stable serialization for a sink. Field set is the frozen wire contract."""
|
|
72
|
+
return {
|
|
73
|
+
"schema_version": self.schema_version,
|
|
74
|
+
"kind": self.kind,
|
|
75
|
+
"board": self.board,
|
|
76
|
+
"seat_key": self.seat_key,
|
|
77
|
+
"vendor_family": self.vendor_family,
|
|
78
|
+
"harness": self.harness,
|
|
79
|
+
"sequence": self.sequence,
|
|
80
|
+
"occurred_at": self.occurred_at,
|
|
81
|
+
"payload": dict(self.payload),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@runtime_checkable
|
|
86
|
+
class EventSink(Protocol):
|
|
87
|
+
"""A destination for advisor-board events. The concrete mapping to Omnigent /
|
|
88
|
+
ui-read-model / state-ledger is ABDOBS; this only fixes the emit surface."""
|
|
89
|
+
|
|
90
|
+
def emit(self, event: AdvisorBoardEvent) -> None: ...
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class NullSink:
|
|
94
|
+
"""Default sink: drops every event. Keeps the ``default`` board's observability
|
|
95
|
+
a no-op (today's behavior: no forwarding) until ABDOBS wires a real sink."""
|
|
96
|
+
|
|
97
|
+
def emit(self, event: AdvisorBoardEvent) -> None: # noqa: D401 - trivial
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def best_effort_forward(sink: EventSink | None, event: AdvisorBoardEvent) -> bool:
|
|
102
|
+
"""Forward ``event`` to ``sink``, swallowing EVERY error.
|
|
103
|
+
|
|
104
|
+
Returns whether the sink accepted the event; a ``None`` sink or any raised
|
|
105
|
+
exception returns ``False`` WITHOUT propagating — forwarding must never delay
|
|
106
|
+
or fail the native leg (IF-0-ABDFREEZE-5). The async dispatch (off the leg's
|
|
107
|
+
critical path) is ABDOBS's to wire; the never-raise guarantee is frozen here.
|
|
108
|
+
"""
|
|
109
|
+
if sink is None:
|
|
110
|
+
return False
|
|
111
|
+
try:
|
|
112
|
+
sink.emit(event)
|
|
113
|
+
return True
|
|
114
|
+
except Exception: # best-effort: an observability failure NEVER touches the leg
|
|
115
|
+
return False
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Advisor Board config — FROZEN FORMAT (IF-0-ABDFREEZE-1).
|
|
2
|
+
#
|
|
3
|
+
# Location: $XDG_CONFIG_HOME/agent-harness/advisor-boards.toml
|
|
4
|
+
# (default ~/.config/agent-harness/advisor-boards.toml)
|
|
5
|
+
#
|
|
6
|
+
# This is the on-disk shape ABDREG's loader parses. It is a fixture only — the
|
|
7
|
+
# loader, board presets, and unknown-key validation land in ABDREG (Phase 2).
|
|
8
|
+
# A board is a named, purpose-tagged, OPEN-ENDED list of seats. A seat is a
|
|
9
|
+
# cognition {model, effort, harness?, lens?, auth?, backing?}, model-first: the
|
|
10
|
+
# harness is a defaulted-but-overridable execution lane.
|
|
11
|
+
|
|
12
|
+
# Optional: the board a bare `advisor-board` invocation resolves to.
|
|
13
|
+
default_board = "default"
|
|
14
|
+
|
|
15
|
+
# The default board reproduces today's 3-leg panel (byte-for-byte back-compat).
|
|
16
|
+
# Its three seats, in panel-leg order, render to today's exact CLI invocations:
|
|
17
|
+
# codex gpt-5.5 effort=max -> -c model_reasoning_effort=xhigh
|
|
18
|
+
# gemini "Gemini 3.1 Pro" effort=high -> model "Gemini 3.1 Pro (High)"
|
|
19
|
+
# claude claude-sonnet-5 effort=max -> --effort max
|
|
20
|
+
[[boards]]
|
|
21
|
+
name = "default"
|
|
22
|
+
purpose = "premerge-review"
|
|
23
|
+
allow_api_key_fallback = false
|
|
24
|
+
|
|
25
|
+
[[boards.seats]]
|
|
26
|
+
model = "gpt-5.5"
|
|
27
|
+
effort = "max"
|
|
28
|
+
harness = "codex" # optional; omit to resolve via default_lane(model)
|
|
29
|
+
auth = "subscription" # subscription (default) | api_key
|
|
30
|
+
backing = "homebrew" # homebrew (default) | omnigent
|
|
31
|
+
|
|
32
|
+
[[boards.seats]]
|
|
33
|
+
model = "Gemini 3.1 Pro"
|
|
34
|
+
effort = "high"
|
|
35
|
+
harness = "gemini"
|
|
36
|
+
|
|
37
|
+
[[boards.seats]]
|
|
38
|
+
model = "claude-sonnet-5"
|
|
39
|
+
effort = "max"
|
|
40
|
+
harness = "claude"
|
|
41
|
+
|
|
42
|
+
# A purpose-tagged preset with an open-ended seat list (ABDREG ships the real
|
|
43
|
+
# presets: default, code-review, brainstorm, doc-edit, legal-review,
|
|
44
|
+
# legal-strategy-review, legal-brainstorm).
|
|
45
|
+
[[boards]]
|
|
46
|
+
name = "code-review"
|
|
47
|
+
purpose = "code-review"
|
|
48
|
+
allow_api_key_fallback = false
|
|
49
|
+
|
|
50
|
+
[[boards.seats]]
|
|
51
|
+
model = "gpt-5.5"
|
|
52
|
+
effort = "max"
|
|
53
|
+
harness = "codex"
|
|
54
|
+
|
|
55
|
+
[[boards.seats]]
|
|
56
|
+
model = "claude-sonnet-5"
|
|
57
|
+
effort = "max"
|
|
58
|
+
harness = "claude"
|
|
59
|
+
lens = "adversarial" # optional review lens/persona
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Shared canonical fixtures (IF-0-ABDFREEZE-2 keystone).
|
|
2
|
+
|
|
3
|
+
ONE fixture set, importable from both ``src`` and ``tests``, that ABDREG
|
|
4
|
+
populates *from* and ABDRESOLVE / ABDHOME test *against* — so the parallel lanes
|
|
5
|
+
never diverge into a mock-vs-real integration cliff.
|
|
6
|
+
|
|
7
|
+
The values here are **golden expectations**, deliberately hard-coded (not derived
|
|
8
|
+
from ``panel_invoker``) so a change to today's panel constants trips
|
|
9
|
+
``tests/test_advisor_board_backcompat.py`` instead of silently re-baselining. The
|
|
10
|
+
back-compat proof re-derives each default seat's invocation and asserts it equals
|
|
11
|
+
today's literals in ``panel_invoker`` (``DEFAULT_LEG_MODELS`` +
|
|
12
|
+
``_API_KEY_VARS``).
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from .schema import (
|
|
17
|
+
AUTH_SUBSCRIPTION,
|
|
18
|
+
BACKING_HOMEBREW,
|
|
19
|
+
Board,
|
|
20
|
+
Seat,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# The built-3 panel legs, in ``panel_invoker.PANEL_LEGS`` order — the order the
|
|
24
|
+
# default board's seats MUST preserve for byte-for-byte back-compat.
|
|
25
|
+
CANONICAL_LEG_ORDER: tuple[str, ...] = ("codex", "gemini", "claude")
|
|
26
|
+
|
|
27
|
+
# The default board's three seats — model-first, effort split out of the model
|
|
28
|
+
# name. These reconstruct today's ``DEFAULT_LEG_MODELS`` under
|
|
29
|
+
# ``harness_mapping.render_seat_invocation``:
|
|
30
|
+
# codex gpt-5.5 + effort max -> ``-c model_reasoning_effort=xhigh``
|
|
31
|
+
# gemini "Gemini 3.1 Pro" + effort high -> model ``"Gemini 3.1 Pro (High)"``
|
|
32
|
+
# claude claude-fable-5 + effort max -> ``--effort max``
|
|
33
|
+
#
|
|
34
|
+
# The claude seat runs Fable (``claude-fable-5``): pre-merge review is a mid-tier
|
|
35
|
+
# decision where being wrong is expensive, so the default review board reviews on
|
|
36
|
+
# Fable, not on the implementer model ``claude-sonnet-5``. This is byte-pinned to
|
|
37
|
+
# ``panel_invoker.DEFAULT_LEG_MODELS["claude"]`` (also Fable) by the golden proof.
|
|
38
|
+
DEFAULT_SEATS: tuple[Seat, ...] = (
|
|
39
|
+
Seat(model="gpt-5.5", effort="max", harness="codex",
|
|
40
|
+
auth=AUTH_SUBSCRIPTION, backing=BACKING_HOMEBREW),
|
|
41
|
+
Seat(model="Gemini 3.1 Pro", effort="high", harness="gemini",
|
|
42
|
+
auth=AUTH_SUBSCRIPTION, backing=BACKING_HOMEBREW),
|
|
43
|
+
Seat(model="claude-fable-5", effort="max", harness="claude",
|
|
44
|
+
auth=AUTH_SUBSCRIPTION, backing=BACKING_HOMEBREW),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
DEFAULT_BOARD: Board = Board(
|
|
48
|
+
name="default",
|
|
49
|
+
purpose="premerge-review",
|
|
50
|
+
seats=DEFAULT_SEATS,
|
|
51
|
+
allow_api_key_fallback=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Golden literals the default seats must reproduce (cross-checked in the
|
|
55
|
+
# back-compat test against the live ``panel_invoker`` constants).
|
|
56
|
+
DEFAULT_SEAT_RENDERED_MODEL: dict[str, str] = {
|
|
57
|
+
"codex": "gpt-5.5",
|
|
58
|
+
"gemini": "Gemini 3.1 Pro (High)",
|
|
59
|
+
"claude": "claude-fable-5",
|
|
60
|
+
}
|
|
61
|
+
DEFAULT_SEAT_EFFORT_ARGS: dict[str, tuple[str, ...]] = {
|
|
62
|
+
"codex": ("-c", "model_reasoning_effort=xhigh"),
|
|
63
|
+
"gemini": (),
|
|
64
|
+
"claude": ("--effort", "max"),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# Canonical (model x harness) pairs ABDREG's matrix + ABDRESOLVE's validation test
|
|
68
|
+
# against. Same-vendor-across-harness (gpt-5.5 on codex and opencode) is VALID and
|
|
69
|
+
# projects to one family; a cross-vendor mismatch (gpt-5.5 on claude) is INVALID.
|
|
70
|
+
CANONICAL_VALID_PAIRS: tuple[tuple[str, str], ...] = (
|
|
71
|
+
("gpt-5.5", "codex"),
|
|
72
|
+
("gpt-5.5", "opencode"),
|
|
73
|
+
("claude-sonnet-5", "claude"),
|
|
74
|
+
("Gemini 3.1 Pro", "gemini"),
|
|
75
|
+
)
|
|
76
|
+
CANONICAL_INVALID_PAIRS: tuple[tuple[str, str], ...] = (
|
|
77
|
+
("gpt-5.5", "claude"), # openai-family model on the claude lane
|
|
78
|
+
("claude-sonnet-5", "codex"), # anthropic model on the codex lane
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# A two-same-vendor-seat board: exercises result re-keying (leg -> seat) and the
|
|
82
|
+
# governed reviewer != author disjointness under model-first (both seats project
|
|
83
|
+
# to ``codex``). Used by ABDRESOLVE / ABDHOME.
|
|
84
|
+
TWO_SAME_VENDOR_BOARD: Board = Board(
|
|
85
|
+
name="two-openai",
|
|
86
|
+
purpose="brainstorm",
|
|
87
|
+
seats=(
|
|
88
|
+
Seat(model="gpt-5.5", effort="high", harness="codex"),
|
|
89
|
+
Seat(model="gpt-5.5", effort="high", harness="opencode"),
|
|
90
|
+
),
|
|
91
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""Per-harness model/effort -> invocation mapping (IF-0-ABDFREEZE-1).
|
|
2
|
+
|
|
3
|
+
The v5 panel-verification found that **per-leg effort is NOT supported today** —
|
|
4
|
+
it is hard-coded per leg (claude ``--effort max`` panel_invoker.py:324, codex
|
|
5
|
+
``model_reasoning_effort=xhigh`` :992) and for the agy/gemini leg **effort is
|
|
6
|
+
baked into the model-name string** (``"Gemini 3.1 Pro (High)"`` :51/:1016). The
|
|
7
|
+
model-first ``{model, effort}`` split therefore needs a per-harness mapping that
|
|
8
|
+
turns a canonical ``(model, effort)`` back into each harness's actual invocation.
|
|
9
|
+
|
|
10
|
+
This module freezes that contract. ABDHOME does the plumbing (reaching the real
|
|
11
|
+
CLI arg lists in `panel_invoker`); it codes against ``render_seat_invocation``.
|
|
12
|
+
The freeze is load-bearing for back-compat: the ``default`` board's three seats
|
|
13
|
+
MUST render to today's exact literals —
|
|
14
|
+
|
|
15
|
+
claude -> effort flag ``--effort max``
|
|
16
|
+
codex -> config override ``-c model_reasoning_effort=xhigh``
|
|
17
|
+
gemini -> model-name embed ``Gemini 3.1 Pro (High)``
|
|
18
|
+
|
|
19
|
+
— proven by ``tests/test_advisor_board_backcompat.py`` against the live
|
|
20
|
+
`panel_invoker` constants (`DEFAULT_LEG_MODELS`, the codex/claude/agy arg forms).
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import re
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
|
|
27
|
+
from .schema import EFFORT_LEVELS
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class EffortMappingError(NotImplementedError):
|
|
31
|
+
"""Raised when a harness's effort mapping is not frozen here (breadth lanes
|
|
32
|
+
land in ABDREG/ABDHOME/ABDOMNI)."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Effort mechanism: HOW the effort reaches the CLI for a harness.
|
|
36
|
+
MECH_FLAG = "flag" # a dedicated flag, e.g. claude ``--effort <level>``
|
|
37
|
+
MECH_CONFIG = "config" # a ``-c key=value`` override, e.g. codex reasoning
|
|
38
|
+
MECH_MODEL_NAME = "model_name" # baked into the model string, e.g. agy/gemini
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class SeatInvocation:
|
|
43
|
+
"""The frozen, harness-specific shape ``(model, effort)`` renders to.
|
|
44
|
+
|
|
45
|
+
``model`` the model string to pass to the CLI (effort-embedded for the
|
|
46
|
+
``model_name`` mechanism; otherwise the model verbatim).
|
|
47
|
+
``effort_args`` extra CLI args carrying effort (empty for ``model_name``).
|
|
48
|
+
``mechanism`` one of ``MECH_FLAG`` / ``MECH_CONFIG`` / ``MECH_MODEL_NAME``.
|
|
49
|
+
``harness`` the execution lane this rendering targets.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
harness: str
|
|
53
|
+
model: str
|
|
54
|
+
effort_args: tuple[str, ...]
|
|
55
|
+
mechanism: str
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# canonical effort -> codex ``model_reasoning_effort`` token. codex's max reasoning
|
|
59
|
+
# is ``xhigh`` (panel_invoker.py:992), so canonical ``max`` -> ``xhigh``.
|
|
60
|
+
_CODEX_EFFORT: dict[str, str] = {
|
|
61
|
+
"low": "low",
|
|
62
|
+
"medium": "medium",
|
|
63
|
+
"high": "high",
|
|
64
|
+
"max": "xhigh",
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
# canonical effort -> the ``(Word)`` token agy/gemini bakes into the model name
|
|
68
|
+
# (panel_invoker.py:1016 uses ``(High)``).
|
|
69
|
+
_GEMINI_EFFORT_WORD: dict[str, str] = {
|
|
70
|
+
"low": "Low",
|
|
71
|
+
"medium": "Medium",
|
|
72
|
+
"high": "High",
|
|
73
|
+
"max": "Max",
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# strip a trailing ``" (Effort)"`` embed so re-rendering is idempotent when a caller
|
|
77
|
+
# passes an already-baked model string (e.g. ``"Gemini 3.1 Pro (High)"``). Matches
|
|
78
|
+
# ONLY the four canonical effort words (the Title-case tokens ``render_gemini_model``
|
|
79
|
+
# emits) — a model whose name genuinely ends in a parenthetical (e.g.
|
|
80
|
+
# ``"Gemini 3.1 Pro (Preview)"``) is a DIFFERENT model and must be left untouched,
|
|
81
|
+
# never silently rewritten to a lower effort.
|
|
82
|
+
_GEMINI_EMBED_RE = re.compile(r"\s*\((?:Low|Medium|High|Max)\)\s*$")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def gemini_base_model(model: str) -> str:
|
|
86
|
+
"""Return the gemini model with any trailing ``(Effort)`` embed removed."""
|
|
87
|
+
return _GEMINI_EMBED_RE.sub("", model or "").strip()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def render_gemini_model(model: str, effort: str) -> str:
|
|
91
|
+
"""``("Gemini 3.1 Pro", "high") -> "Gemini 3.1 Pro (High)"`` — the agy leg's
|
|
92
|
+
effort-in-the-model-name special case. Idempotent on an already-baked model."""
|
|
93
|
+
_require_effort(effort)
|
|
94
|
+
return f"{gemini_base_model(model)} ({_GEMINI_EFFORT_WORD[effort]})"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _require_effort(effort: str) -> None:
|
|
98
|
+
if effort not in EFFORT_LEVELS:
|
|
99
|
+
raise ValueError(f"effort {effort!r} not in {EFFORT_LEVELS}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def render_seat_invocation(harness: str, model: str, effort: str) -> SeatInvocation:
|
|
103
|
+
"""Freeze: turn a canonical ``(harness, model, effort)`` into its CLI invocation.
|
|
104
|
+
|
|
105
|
+
Only the built-3 homebrew lanes (claude / codex / gemini) are frozen here —
|
|
106
|
+
they are what the ``default`` board's back-compat proof rides on. Breadth
|
|
107
|
+
lanes (opencode / pi / cursor / amp) raise ``EffortMappingError`` until
|
|
108
|
+
ABDREG/ABDHOME/ABDOMNI populate them; a board with an unmapped lane degrades
|
|
109
|
+
skip-with-warning, never silently drops effort.
|
|
110
|
+
"""
|
|
111
|
+
_require_effort(effort)
|
|
112
|
+
lane = (harness or "").lower()
|
|
113
|
+
if lane == "claude":
|
|
114
|
+
# panel_invoker.py:322-325 -> ``--model <model> --effort <level>``
|
|
115
|
+
return SeatInvocation(lane, model, ("--effort", effort), MECH_FLAG)
|
|
116
|
+
if lane == "codex":
|
|
117
|
+
# panel_invoker.py:991-992 -> ``--model <model> -c model_reasoning_effort=<tok>``
|
|
118
|
+
token = _CODEX_EFFORT[effort]
|
|
119
|
+
return SeatInvocation(lane, model, ("-c", f"model_reasoning_effort={token}"), MECH_CONFIG)
|
|
120
|
+
if lane == "gemini":
|
|
121
|
+
# panel_invoker.py:1016 -> effort baked into ``--model "<base> (Word)"``
|
|
122
|
+
return SeatInvocation(lane, render_gemini_model(model, effort), (), MECH_MODEL_NAME)
|
|
123
|
+
raise EffortMappingError(
|
|
124
|
+
f"effort mapping for harness {harness!r} is populated in ABDREG/ABDHOME/ABDOMNI"
|
|
125
|
+
)
|