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,305 @@
|
|
|
1
|
+
"""CS-0.5 -- the `.consiliency/` scaffolder (first-writer).
|
|
2
|
+
|
|
3
|
+
`phase-loop consiliency-scaffold --repo <path> --archetype <name>` writes a
|
|
4
|
+
schema-valid `.consiliency/` layout: a manifest declaring the archetype(s) +
|
|
5
|
+
governed-doc allowlist, a contract-version/status artifact, an (initially
|
|
6
|
+
empty) interface declaration, and presence-only L0 stubs for the archetype's
|
|
7
|
+
required baseline docs.
|
|
8
|
+
|
|
9
|
+
Design constraints (roadmap CS-0.5):
|
|
10
|
+
|
|
11
|
+
* **Additive, never destructive.** This module never modifies or deletes
|
|
12
|
+
`.phase-loop/` or `.pipeline/`, and it never overwrites a file that already
|
|
13
|
+
exists -- "first-writer" means the scaffolder only fills gaps. Re-running it
|
|
14
|
+
on an already-scaffolded (or hand-edited) repo is a safe no-op for anything
|
|
15
|
+
present.
|
|
16
|
+
* **No fake projections.** Where the required-documents registry marks a doc
|
|
17
|
+
``l0_stub_allowed: true`` (the common case), the scaffolder writes a minimal
|
|
18
|
+
presence-only stub with an explicit "authored zone" -- honest about being
|
|
19
|
+
unauthored, never a hand-written imitation of real content. Where a doc is
|
|
20
|
+
``l0_stub_allowed: false`` (``readme``, ``license``, and the ``document``
|
|
21
|
+
archetype's ``document-index``), the scaffolder never invents content: it
|
|
22
|
+
references the file if the repo already has it at the conventional path,
|
|
23
|
+
and otherwise only *declares* the manifest entry, leaving the gap for a
|
|
24
|
+
human -- the CS-0.6 presence gate reports that gap as a WARN finding.
|
|
25
|
+
* **Vendored schemas only.** All shapes come from ``consiliency_contract``
|
|
26
|
+
(the published shared contract package); nothing here re-implements or
|
|
27
|
+
copies schema literals.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import json
|
|
32
|
+
import re
|
|
33
|
+
import subprocess
|
|
34
|
+
from dataclasses import dataclass
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
from jsonschema import Draft202012Validator
|
|
39
|
+
|
|
40
|
+
from .consiliency_layout import (
|
|
41
|
+
ARCHETYPE_IDS,
|
|
42
|
+
MODIFIER_IDS,
|
|
43
|
+
RequiredDocRow,
|
|
44
|
+
compose_required_documents,
|
|
45
|
+
consiliency_root,
|
|
46
|
+
contract_version_status_schema,
|
|
47
|
+
find_consiliency_manifest,
|
|
48
|
+
installed_contract_version,
|
|
49
|
+
interface_declaration_schema,
|
|
50
|
+
interfaces_path,
|
|
51
|
+
manifest_path,
|
|
52
|
+
manifest_schema,
|
|
53
|
+
status_path,
|
|
54
|
+
)
|
|
55
|
+
from .models import utc_now
|
|
56
|
+
|
|
57
|
+
#: Docs the scaffolder generates as REAL minimal artifacts (not stubs) -- their
|
|
58
|
+
#: content is true/current information (installed package version, an empty
|
|
59
|
+
#: declared-edge set), never a placeholder standing in for unauthored prose.
|
|
60
|
+
_MACHINE_REAL_IDS = ("contract-version-status", "interface-declaration")
|
|
61
|
+
|
|
62
|
+
#: Well-known repo-root filenames for the handful of baseline docs that have
|
|
63
|
+
#: an obvious conventional home. Everything else is scaffolded under
|
|
64
|
+
#: `.consiliency/docs/<id>.md`, a namespace that can never collide with a
|
|
65
|
+
#: repo's own doc layout.
|
|
66
|
+
_ROOT_CONVENTION_PATHS: dict[str, str] = {
|
|
67
|
+
"readme": "README.md",
|
|
68
|
+
"license": "LICENSE",
|
|
69
|
+
"contributing": "CONTRIBUTING.md",
|
|
70
|
+
"changelog": "CHANGELOG.md",
|
|
71
|
+
"codeowners": "CODEOWNERS",
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_SLUG_RE = re.compile(r"[^a-z0-9_.-]+")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _slugify(name: str) -> str:
|
|
78
|
+
slug = _SLUG_RE.sub("-", name.strip().lower()).strip("-.")
|
|
79
|
+
if not slug or not slug[0].isalnum():
|
|
80
|
+
slug = f"repo-{slug}" if slug else "repo"
|
|
81
|
+
return slug
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _default_branch(repo: Path) -> str:
|
|
85
|
+
try:
|
|
86
|
+
ref = subprocess.check_output(
|
|
87
|
+
["git", "-C", str(repo), "symbolic-ref", "--short", "HEAD"],
|
|
88
|
+
text=True,
|
|
89
|
+
stderr=subprocess.DEVNULL,
|
|
90
|
+
).strip()
|
|
91
|
+
if ref:
|
|
92
|
+
return ref
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
return "main"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _doc_target_path(row: RequiredDocRow) -> str:
|
|
99
|
+
if row.id in _MACHINE_REAL_IDS:
|
|
100
|
+
return f".consiliency/{'status.json' if row.id == 'contract-version-status' else 'interfaces.json'}"
|
|
101
|
+
if row.id in _ROOT_CONVENTION_PATHS:
|
|
102
|
+
return _ROOT_CONVENTION_PATHS[row.id]
|
|
103
|
+
return f".consiliency/docs/{row.id}.md"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _stub_body(row: RequiredDocRow) -> str:
|
|
107
|
+
title = row.id.replace("-", " ").title()
|
|
108
|
+
note = row.l0_note or (
|
|
109
|
+
f"This is an L0 presence-only stub. Its existence satisfies the Consiliency "
|
|
110
|
+
f"`{row.id}` document surface at maturity `presence-only` -- it declares that "
|
|
111
|
+
"this surface is tracked, not that its content has been authored."
|
|
112
|
+
)
|
|
113
|
+
if row.id == "codeowners":
|
|
114
|
+
return (
|
|
115
|
+
f"# Generated by: phase-loop consiliency-scaffold (doc-id={row.id}, maturity=presence-only)\n"
|
|
116
|
+
f"# {note}\n"
|
|
117
|
+
"# consiliency:authored-zone:start\n"
|
|
118
|
+
"# TODO: author real CODEOWNERS entries.\n"
|
|
119
|
+
"# consiliency:authored-zone:end\n"
|
|
120
|
+
)
|
|
121
|
+
return (
|
|
122
|
+
f"<!-- consiliency:generated-by=phase-loop consiliency-scaffold -->\n"
|
|
123
|
+
f"<!-- consiliency:doc-id={row.id} -->\n"
|
|
124
|
+
f"<!-- consiliency:maturity=presence-only -->\n\n"
|
|
125
|
+
f"# {title}\n\n"
|
|
126
|
+
f"{note}\n\n"
|
|
127
|
+
"<!-- consiliency:authored-zone:start -->\n"
|
|
128
|
+
"TODO: author this document's real content.\n"
|
|
129
|
+
"<!-- consiliency:authored-zone:end -->\n"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(frozen=True)
|
|
134
|
+
class ScaffoldResult:
|
|
135
|
+
repo: Path
|
|
136
|
+
dry_run: bool
|
|
137
|
+
manifest_path: Path
|
|
138
|
+
manifest_written: bool
|
|
139
|
+
created_paths: tuple[str, ...]
|
|
140
|
+
referenced_paths: tuple[str, ...]
|
|
141
|
+
declared_missing_paths: tuple[str, ...]
|
|
142
|
+
already_present: bool
|
|
143
|
+
|
|
144
|
+
def to_json(self) -> dict[str, Any]:
|
|
145
|
+
return {
|
|
146
|
+
"repo": str(self.repo),
|
|
147
|
+
"dry_run": self.dry_run,
|
|
148
|
+
"manifest_path": str(self.manifest_path),
|
|
149
|
+
"manifest_written": self.manifest_written,
|
|
150
|
+
"created_paths": list(self.created_paths),
|
|
151
|
+
"referenced_paths": list(self.referenced_paths),
|
|
152
|
+
"declared_missing_paths": list(self.declared_missing_paths),
|
|
153
|
+
"already_present": self.already_present,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ScaffoldError(ValueError):
|
|
158
|
+
"""Bad scaffold input (unknown archetype/modifier, baseline-only + archetypes, ...)."""
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def scaffold(
|
|
162
|
+
repo: str | Path,
|
|
163
|
+
*,
|
|
164
|
+
mode: str,
|
|
165
|
+
archetypes: tuple[str, ...] = (),
|
|
166
|
+
modifiers: tuple[str, ...] = (),
|
|
167
|
+
repo_id: str | None = None,
|
|
168
|
+
display_name: str | None = None,
|
|
169
|
+
dry_run: bool = False,
|
|
170
|
+
) -> ScaffoldResult:
|
|
171
|
+
repo = Path(repo)
|
|
172
|
+
if mode not in ("baseline-only", "archetyped"):
|
|
173
|
+
raise ScaffoldError(f"invalid mode: {mode!r} (expected 'baseline-only' or 'archetyped')")
|
|
174
|
+
if mode == "baseline-only" and (archetypes or modifiers):
|
|
175
|
+
raise ScaffoldError("baseline-only mode does not accept --archetype/--modifier")
|
|
176
|
+
if mode == "archetyped" and not archetypes:
|
|
177
|
+
raise ScaffoldError("archetyped mode requires at least one --archetype")
|
|
178
|
+
unknown_archetypes = sorted(set(archetypes) - set(ARCHETYPE_IDS))
|
|
179
|
+
if unknown_archetypes:
|
|
180
|
+
raise ScaffoldError(f"unknown archetype(s): {unknown_archetypes}; known: {list(ARCHETYPE_IDS)}")
|
|
181
|
+
unknown_modifiers = sorted(set(modifiers) - set(MODIFIER_IDS))
|
|
182
|
+
if unknown_modifiers:
|
|
183
|
+
raise ScaffoldError(f"unknown modifier(s): {unknown_modifiers}; known: {list(MODIFIER_IDS)}")
|
|
184
|
+
|
|
185
|
+
existing_manifest = find_consiliency_manifest(repo)
|
|
186
|
+
already_present = existing_manifest is not None
|
|
187
|
+
target_manifest_path = manifest_path(repo)
|
|
188
|
+
|
|
189
|
+
rows = compose_required_documents(mode=mode, archetypes=archetypes, modifiers=modifiers)
|
|
190
|
+
|
|
191
|
+
created: list[str] = []
|
|
192
|
+
referenced: list[str] = []
|
|
193
|
+
declared_missing: list[str] = []
|
|
194
|
+
documents: list[dict[str, Any]] = []
|
|
195
|
+
|
|
196
|
+
for row in sorted(rows, key=lambda r: r.id):
|
|
197
|
+
rel_path = _doc_target_path(row)
|
|
198
|
+
abs_path = repo / rel_path
|
|
199
|
+
documents.append(
|
|
200
|
+
{
|
|
201
|
+
"id": row.id,
|
|
202
|
+
"class": row.doc_class,
|
|
203
|
+
"path": rel_path,
|
|
204
|
+
"maturity": "presence-only",
|
|
205
|
+
"target_level": "L0",
|
|
206
|
+
"required": row.required,
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
if abs_path.exists():
|
|
210
|
+
referenced.append(rel_path)
|
|
211
|
+
continue
|
|
212
|
+
if row.id in _MACHINE_REAL_IDS or row.l0_stub_allowed:
|
|
213
|
+
created.append(rel_path)
|
|
214
|
+
if not dry_run:
|
|
215
|
+
abs_path.parent.mkdir(parents=True, exist_ok=True)
|
|
216
|
+
if row.id == "contract-version-status":
|
|
217
|
+
abs_path.write_text(
|
|
218
|
+
json.dumps(_status_payload(), indent=2, sort_keys=True) + "\n", encoding="utf-8"
|
|
219
|
+
)
|
|
220
|
+
elif row.id == "interface-declaration":
|
|
221
|
+
abs_path.write_text(
|
|
222
|
+
json.dumps(_interfaces_payload(repo_id or _slugify(repo.name)), indent=2, sort_keys=True) + "\n",
|
|
223
|
+
encoding="utf-8",
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
abs_path.write_text(_stub_body(row), encoding="utf-8")
|
|
227
|
+
else:
|
|
228
|
+
# l0_stub_allowed: false and nothing on disk -- do NOT fabricate.
|
|
229
|
+
# Declared in the manifest; the presence gate reports the gap.
|
|
230
|
+
declared_missing.append(rel_path)
|
|
231
|
+
|
|
232
|
+
manifest = {
|
|
233
|
+
"schema": "consiliency.manifest.v1",
|
|
234
|
+
"contract_version": installed_contract_version(),
|
|
235
|
+
"repo": {
|
|
236
|
+
"id": repo_id or _slugify(repo.name),
|
|
237
|
+
"display_name": display_name or repo.name,
|
|
238
|
+
"default_branch": _default_branch(repo),
|
|
239
|
+
},
|
|
240
|
+
"declaration": (
|
|
241
|
+
{"mode": "baseline-only"}
|
|
242
|
+
if mode == "baseline-only"
|
|
243
|
+
else {
|
|
244
|
+
"mode": "archetyped",
|
|
245
|
+
"archetypes": sorted(archetypes),
|
|
246
|
+
**({"modifiers": sorted(modifiers)} if modifiers else {}),
|
|
247
|
+
}
|
|
248
|
+
),
|
|
249
|
+
"documents": documents,
|
|
250
|
+
"interfaces": ".consiliency/interfaces.json",
|
|
251
|
+
}
|
|
252
|
+
Draft202012Validator(manifest_schema()).validate(manifest)
|
|
253
|
+
|
|
254
|
+
manifest_written = False
|
|
255
|
+
if not already_present and not dry_run:
|
|
256
|
+
target_manifest_path.parent.mkdir(parents=True, exist_ok=True)
|
|
257
|
+
target_manifest_path.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
258
|
+
manifest_written = True
|
|
259
|
+
elif not already_present:
|
|
260
|
+
manifest_written = True # what WOULD be written, for dry-run reporting
|
|
261
|
+
|
|
262
|
+
return ScaffoldResult(
|
|
263
|
+
repo=repo,
|
|
264
|
+
dry_run=dry_run,
|
|
265
|
+
manifest_path=target_manifest_path,
|
|
266
|
+
manifest_written=manifest_written,
|
|
267
|
+
created_paths=tuple(created),
|
|
268
|
+
referenced_paths=tuple(referenced),
|
|
269
|
+
declared_missing_paths=tuple(declared_missing),
|
|
270
|
+
already_present=already_present,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _status_payload() -> dict[str, Any]:
|
|
275
|
+
version = installed_contract_version()
|
|
276
|
+
return {
|
|
277
|
+
"schema": "consiliency.contract_version_status.v1",
|
|
278
|
+
"package": {"name": "consiliency-contract", "version": version},
|
|
279
|
+
"repo_contract_version": version,
|
|
280
|
+
"compatibility": "compatible",
|
|
281
|
+
"maturity": "presence-only",
|
|
282
|
+
"checked_at": utc_now(),
|
|
283
|
+
"evidence": [],
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _interfaces_payload(repo_id: str) -> dict[str, Any]:
|
|
288
|
+
return {
|
|
289
|
+
"schema": "consiliency.interface_declaration.v1",
|
|
290
|
+
"repo_id": repo_id,
|
|
291
|
+
"realized_edges": [],
|
|
292
|
+
"promised_edges": [],
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# Defensive self-checks -- the schemas ARE the contract; validate our own
|
|
297
|
+
# generated shapes against them so a drift in this module fails loud in tests
|
|
298
|
+
# rather than silently shipping a manifest the gates can't parse.
|
|
299
|
+
def _validate_generated_artifacts(repo: Path) -> None:
|
|
300
|
+
status_file = status_path(repo)
|
|
301
|
+
if status_file.is_file():
|
|
302
|
+
Draft202012Validator(contract_version_status_schema()).validate(json.loads(status_file.read_text(encoding="utf-8")))
|
|
303
|
+
interfaces_file = interfaces_path(repo)
|
|
304
|
+
if interfaces_file.is_file():
|
|
305
|
+
Draft202012Validator(interface_declaration_schema()).validate(json.loads(interfaces_file.read_text(encoding="utf-8")))
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""Cross-repo consumption-channel descriptor and injection primitive.
|
|
2
|
+
|
|
3
|
+
IF-0-P2-2 contract: every cross-repo dependency edge declares HOW the
|
|
4
|
+
downstream workspace references the upstream — via a manifest-file version pin,
|
|
5
|
+
a ``git submodule``, or a workspace/path override. **NOT a git rebase**: two
|
|
6
|
+
repos have unrelated git histories.
|
|
7
|
+
|
|
8
|
+
The load-bearing primitive is :func:`set_upstream_ref`, which the coordinator
|
|
9
|
+
calls (with the upstream draft branch ref in P3 and the upstream merged SHA in
|
|
10
|
+
P4) **before** invoking the unchanged per-repo ``run_loop`` in the downstream
|
|
11
|
+
workspace. This is how an unchanged ``run_loop`` can consume the upstream at
|
|
12
|
+
all.
|
|
13
|
+
|
|
14
|
+
Channel kinds (closed set — no plugin system):
|
|
15
|
+
|
|
16
|
+
``pin``
|
|
17
|
+
A manifest-file version pin: the executor rewrites a file the downstream
|
|
18
|
+
build ACTUALLY reads (e.g. ``requirements.txt``, ``package.json``, a
|
|
19
|
+
lockfile, a plain version file).
|
|
20
|
+
|
|
21
|
+
Two forms:
|
|
22
|
+
``pin file=<path>`` — plain version/ref file: writes ``ref``
|
|
23
|
+
as the sole file content.
|
|
24
|
+
``pin file=<path> key=<a.b.c>`` — JSON manifest: loads existing JSON,
|
|
25
|
+
sets the nested dotted key to ``ref``,
|
|
26
|
+
writes back with 2-space indent.
|
|
27
|
+
|
|
28
|
+
Required param: ``file`` (repo-relative path of the file to rewrite).
|
|
29
|
+
Optional param: ``key`` (dotted JSON key; absent → plain file).
|
|
30
|
+
|
|
31
|
+
``submodule``
|
|
32
|
+
A ``git submodule``. Params: ``path`` (submodule path relative to the
|
|
33
|
+
downstream workspace root).
|
|
34
|
+
|
|
35
|
+
``workspace``
|
|
36
|
+
A workspace/path override (e.g. a ``[tool.uv.sources]`` workspace entry or
|
|
37
|
+
a Cargo workspace path dep). Params: ``path`` (the override path).
|
|
38
|
+
**Rejected at train validation (T-E)**: workspace injection is not
|
|
39
|
+
implemented for real consumption and is rejected at preflight.
|
|
40
|
+
|
|
41
|
+
``none``
|
|
42
|
+
No upstream dependency; declared explicitly for root nodes.
|
|
43
|
+
|
|
44
|
+
Zero external deps (stdlib only).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from __future__ import annotations
|
|
48
|
+
|
|
49
|
+
import json
|
|
50
|
+
import subprocess
|
|
51
|
+
from dataclasses import dataclass, field
|
|
52
|
+
from pathlib import Path
|
|
53
|
+
from typing import Callable, Dict, List, Literal, Optional
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Channel descriptor — IF-0-P2-2
|
|
57
|
+
|
|
58
|
+
ChannelKind = Literal["pin", "submodule", "workspace", "none", "order-only"]
|
|
59
|
+
|
|
60
|
+
VALID_CHANNEL_KINDS: frozenset[str] = frozenset(
|
|
61
|
+
{"pin", "submodule", "workspace", "none", "order-only"}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
_NONE_VALUES = frozenset({"(none)", "none", ""})
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class ChannelDescriptor:
|
|
69
|
+
"""Per-edge consumption-channel descriptor (IF-0-P2-2).
|
|
70
|
+
|
|
71
|
+
Attributes:
|
|
72
|
+
kind: One of ``pin``, ``submodule``, ``workspace``, or ``none``.
|
|
73
|
+
params: Kind-specific parameters (see module docstring).
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
kind: ChannelKind
|
|
77
|
+
params: Dict[str, str] = field(default_factory=dict, compare=True, hash=False)
|
|
78
|
+
|
|
79
|
+
def __hash__(self) -> int:
|
|
80
|
+
return hash((self.kind, tuple(sorted(self.params.items()))))
|
|
81
|
+
|
|
82
|
+
def __repr__(self) -> str: # pragma: no cover
|
|
83
|
+
if self.kind == "none":
|
|
84
|
+
return "ChannelDescriptor(none)"
|
|
85
|
+
params_str = " ".join(f"{k}={v}" for k, v in sorted(self.params.items()))
|
|
86
|
+
return f"ChannelDescriptor({self.kind} {params_str})"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
# Parser
|
|
91
|
+
|
|
92
|
+
def parse_channel_line(raw: str) -> ChannelDescriptor:
|
|
93
|
+
"""Parse a ``**Channel:**`` field value into a :class:`ChannelDescriptor`.
|
|
94
|
+
|
|
95
|
+
Accepted forms::
|
|
96
|
+
|
|
97
|
+
(none) → ChannelDescriptor(kind="none")
|
|
98
|
+
submodule path=vendor/consiliency-portal
|
|
99
|
+
pin file=manifest.json → plain version/ref file
|
|
100
|
+
pin file=manifest.json key=deps.schema → JSON manifest, dotted key
|
|
101
|
+
workspace path=../mylib
|
|
102
|
+
|
|
103
|
+
Raises :exc:`ValueError` on unrecognised channel kind or missing required params.
|
|
104
|
+
"""
|
|
105
|
+
stripped = raw.strip()
|
|
106
|
+
if stripped.lower() in _NONE_VALUES:
|
|
107
|
+
return ChannelDescriptor(kind="none", params={})
|
|
108
|
+
|
|
109
|
+
parts = stripped.split()
|
|
110
|
+
kind = parts[0].lower()
|
|
111
|
+
if kind not in VALID_CHANNEL_KINDS or kind == "none":
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"unknown channel kind '{kind}'; expected one of: pin, submodule, "
|
|
114
|
+
f"workspace, order-only"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
params: Dict[str, str] = {}
|
|
118
|
+
for token in parts[1:]:
|
|
119
|
+
if "=" in token:
|
|
120
|
+
k, _, v = token.partition("=")
|
|
121
|
+
params[k.strip()] = v.strip()
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"channel param '{token}' has no '='; expected 'key=value' form"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
_validate_params(kind, params, raw) # type: ignore[arg-type]
|
|
128
|
+
return ChannelDescriptor(kind=kind, params=params) # type: ignore[arg-type]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _validate_params(kind: ChannelKind, params: Dict[str, str], raw: str) -> None:
|
|
132
|
+
if kind == "pin":
|
|
133
|
+
if "file" not in params:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
f"pin channel requires a 'file' param (the manifest file that gets "
|
|
136
|
+
f"rewritten with the upstream ref); got: {raw!r}"
|
|
137
|
+
)
|
|
138
|
+
elif kind in ("submodule", "workspace"):
|
|
139
|
+
if "path" not in params:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"{kind} channel requires a 'path' param; got: {raw!r}"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
# Injection primitive — IF-0-P2-2
|
|
147
|
+
|
|
148
|
+
# The git/fs boundary is injectable for tests (stub it; never call real git in
|
|
149
|
+
# unit tests). The protocol is a callable:
|
|
150
|
+
# executor(workspace: Path, kind: ChannelKind, params: dict, ref: str) -> List[str]
|
|
151
|
+
# The return value is the list of workspace-relative paths modified (for the
|
|
152
|
+
# coordinator to union into the downstream's published owned_paths).
|
|
153
|
+
|
|
154
|
+
ChannelExecutor = Callable[[Path, str, Dict[str, str], str], List[str]]
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _assert_within_workspace(resolved: Path, workspace_resolved: Path, param: str) -> None:
|
|
158
|
+
"""Fail loud if ``resolved`` escapes ``workspace_resolved``.
|
|
159
|
+
|
|
160
|
+
Raises :exc:`ValueError` if the resolved path is not strictly within the
|
|
161
|
+
workspace root (``../`` traversal or absolute path that escapes the tree).
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
resolved.relative_to(workspace_resolved)
|
|
165
|
+
except ValueError:
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"channel param {param!r} resolves to {str(resolved)!r}, which is "
|
|
168
|
+
f"outside the workspace {str(workspace_resolved)!r}; "
|
|
169
|
+
f"refusing to read/write outside the workspace (path containment violation)"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class UnsupportedChannelKind(ValueError):
|
|
174
|
+
"""Raised by the live executor when a channel kind is valid in the schema but
|
|
175
|
+
not yet implemented for real consumption in this MVP.
|
|
176
|
+
|
|
177
|
+
Using a hollow sentinel file (written but never read by the downstream
|
|
178
|
+
build) would silently build the downstream against the absent upstream,
|
|
179
|
+
corrupting the whole train. We fail loud instead.
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _default_executor(
|
|
184
|
+
workspace: Path,
|
|
185
|
+
kind: str,
|
|
186
|
+
params: Dict[str, str],
|
|
187
|
+
ref: str,
|
|
188
|
+
) -> List[str]:
|
|
189
|
+
"""Default live executor — runs real git/fs operations.
|
|
190
|
+
|
|
191
|
+
Channel support:
|
|
192
|
+
``submodule`` — git fetch + checkout; the downstream build ACTUALLY
|
|
193
|
+
consumes the injected ref (submodule HEAD is updated).
|
|
194
|
+
``pin`` — rewrites the manifest file the downstream build reads:
|
|
195
|
+
- ``file`` only → writes ``ref`` as the plain file content (+ newline).
|
|
196
|
+
- ``file`` + ``key`` → loads existing JSON, sets the nested dotted key
|
|
197
|
+
to ``ref``, writes back with 2-space indent + trailing newline.
|
|
198
|
+
The downstream build reads the file directly, so injection is real.
|
|
199
|
+
``workspace`` — NOT IMPLEMENTED for real consumption; raises
|
|
200
|
+
:exc:`UnsupportedChannelKind`. Workspace edges are rejected at train
|
|
201
|
+
validation (T-E) before any executor is reached.
|
|
202
|
+
|
|
203
|
+
Returns the workspace-relative paths modified (for the coordinator to union
|
|
204
|
+
into the downstream's published owned_paths so pin/submodule changes always
|
|
205
|
+
ship in the PR even if run_loop's snapshot excludes them).
|
|
206
|
+
|
|
207
|
+
Hardening:
|
|
208
|
+
- Path containment: rejects ``file``/``path`` params that escape the
|
|
209
|
+
workspace root via ``../`` traversal or absolute paths.
|
|
210
|
+
- JSON key safety: rejects empty or malformed ``key`` params; raises when
|
|
211
|
+
a non-dict intermediate value would be silently overwritten.
|
|
212
|
+
|
|
213
|
+
Stubbing the executor (``_executor=stub``) is the correct approach for
|
|
214
|
+
tests that exercise workspace channel kinds (which remain unimplemented).
|
|
215
|
+
"""
|
|
216
|
+
workspace_resolved = workspace.resolve()
|
|
217
|
+
|
|
218
|
+
if kind == "submodule":
|
|
219
|
+
submodule_path = params["path"]
|
|
220
|
+
# Path containment: reject ../- or absolute-escaping params.
|
|
221
|
+
resolved = (workspace / submodule_path).resolve()
|
|
222
|
+
_assert_within_workspace(resolved, workspace_resolved, submodule_path)
|
|
223
|
+
# Dereference the submodule to the given ref so the downstream build
|
|
224
|
+
# actually runs against the injected upstream content.
|
|
225
|
+
subprocess.run(
|
|
226
|
+
["git", "fetch", "origin"],
|
|
227
|
+
cwd=workspace / submodule_path,
|
|
228
|
+
check=True,
|
|
229
|
+
)
|
|
230
|
+
subprocess.run(
|
|
231
|
+
["git", "checkout", ref],
|
|
232
|
+
cwd=workspace / submodule_path,
|
|
233
|
+
check=True,
|
|
234
|
+
)
|
|
235
|
+
return [submodule_path]
|
|
236
|
+
elif kind == "pin":
|
|
237
|
+
file_param = params["file"]
|
|
238
|
+
file_path = workspace / file_param
|
|
239
|
+
# Path containment: reject ../- or absolute-escaping params.
|
|
240
|
+
resolved = file_path.resolve()
|
|
241
|
+
_assert_within_workspace(resolved, workspace_resolved, file_param)
|
|
242
|
+
key = params.get("key")
|
|
243
|
+
if key is not None:
|
|
244
|
+
# Validate the key: reject empty or malformed dotted paths.
|
|
245
|
+
if not key or not all(part for part in key.split(".")):
|
|
246
|
+
raise ValueError(
|
|
247
|
+
f"pin 'key' param is empty or malformed: {key!r}; "
|
|
248
|
+
f"expected a non-empty dotted path like 'deps.schema'"
|
|
249
|
+
)
|
|
250
|
+
# JSON manifest: load existing file (or start with empty dict), set
|
|
251
|
+
# the nested dotted key to ref, write back with consistent indent.
|
|
252
|
+
data: Dict = json.loads(file_path.read_text(encoding="utf-8")) if file_path.exists() else {}
|
|
253
|
+
keys = key.split(".")
|
|
254
|
+
node: Dict = data
|
|
255
|
+
for k in keys[:-1]:
|
|
256
|
+
if k not in node:
|
|
257
|
+
node[k] = {}
|
|
258
|
+
elif not isinstance(node[k], dict):
|
|
259
|
+
# Safety: raise rather than silently clobber a non-dict value.
|
|
260
|
+
raise ValueError(
|
|
261
|
+
f"cannot set dotted key {key!r}: intermediate key {k!r} "
|
|
262
|
+
f"exists but is not a dict (got {type(node[k]).__name__!r}); "
|
|
263
|
+
f"refusing to silently overwrite"
|
|
264
|
+
)
|
|
265
|
+
node = node[k]
|
|
266
|
+
node[keys[-1]] = ref
|
|
267
|
+
file_path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
|
|
268
|
+
else:
|
|
269
|
+
# Plain version/ref file: write ref as the sole content.
|
|
270
|
+
file_path.write_text(ref + "\n", encoding="utf-8")
|
|
271
|
+
return [file_param]
|
|
272
|
+
elif kind == "workspace":
|
|
273
|
+
raise UnsupportedChannelKind(
|
|
274
|
+
f"'workspace' channel injection is not implemented for real consumption "
|
|
275
|
+
f"(path={params.get('path')!r}). Workspace edges are rejected at train "
|
|
276
|
+
f"validation (T-E) before reaching the executor; a workspace channel in a "
|
|
277
|
+
f"live train means the train roadmap was not validated — check preflight."
|
|
278
|
+
)
|
|
279
|
+
elif kind == "order-only":
|
|
280
|
+
# #47: an order-only edge carries NO physical channel — the downstream does
|
|
281
|
+
# not consume the upstream artifact, so there is nothing to inject. The
|
|
282
|
+
# coordinator skips set_upstream_ref for order-only edges; reaching the
|
|
283
|
+
# executor here means a caller forgot to skip — fail loud, never no-op.
|
|
284
|
+
raise UnsupportedChannelKind(
|
|
285
|
+
"'order-only' edges carry no consumption channel and must never be "
|
|
286
|
+
"injected; the train coordinator skips set_upstream_ref for them "
|
|
287
|
+
"(they enforce merge order only)."
|
|
288
|
+
)
|
|
289
|
+
else:
|
|
290
|
+
raise ValueError(f"unknown channel kind for executor: {kind!r}")
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def set_upstream_ref(
|
|
294
|
+
workspace: Path,
|
|
295
|
+
channel: ChannelDescriptor,
|
|
296
|
+
ref: str,
|
|
297
|
+
*,
|
|
298
|
+
_executor: Optional[ChannelExecutor] = None,
|
|
299
|
+
) -> List[str]:
|
|
300
|
+
"""Re-resolve the downstream workspace's dependency channel to ``ref``.
|
|
301
|
+
|
|
302
|
+
This is the **load-bearing injection primitive** (IF-0-P2-2): the
|
|
303
|
+
coordinator calls this *before* invoking the unchanged per-repo
|
|
304
|
+
``run_loop`` to point the downstream's consumption channel at a specific
|
|
305
|
+
upstream ref — the draft branch ``head_sha`` in P3 and the upstream
|
|
306
|
+
**merge SHA** in P4.
|
|
307
|
+
|
|
308
|
+
Returns the workspace-relative paths modified by the injection (e.g.
|
|
309
|
+
``["manifest.json"]`` for a pin, ``["vendor/repo-a"]`` for a submodule).
|
|
310
|
+
The coordinator unions these into the downstream node's ``owned_paths``
|
|
311
|
+
before publishing so the pin/submodule change always ships in the PR even
|
|
312
|
+
if ``run_loop``'s snapshot excludes the injected file.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
workspace: Absolute path to the downstream repo's worktree.
|
|
316
|
+
channel: The :class:`ChannelDescriptor` for this edge.
|
|
317
|
+
ref: The upstream ref or SHA to pin the channel to.
|
|
318
|
+
_executor: Optional injectable executor (for testing). Defaults to
|
|
319
|
+
the live git/fs executor.
|
|
320
|
+
|
|
321
|
+
Raises:
|
|
322
|
+
ValueError: If the channel kind is ``none`` (root nodes have no
|
|
323
|
+
channel to re-resolve) or unknown.
|
|
324
|
+
"""
|
|
325
|
+
if channel.kind == "none":
|
|
326
|
+
raise ValueError(
|
|
327
|
+
"set_upstream_ref called on a 'none' channel — root nodes have no "
|
|
328
|
+
"upstream dependency to resolve"
|
|
329
|
+
)
|
|
330
|
+
executor = _executor if _executor is not None else _default_executor
|
|
331
|
+
return executor(workspace, channel.kind, dict(channel.params), ref) or []
|