platformforge 0.1.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.
- platformforge/__init__.py +7 -0
- platformforge/__main__.py +5 -0
- platformforge/_forge_portal.py +913 -0
- platformforge/_graphstudio.py +376 -0
- platformforge/_graphview.py +181 -0
- platformforge/_installkit.py +1706 -0
- platformforge/agents/__init__.py +156 -0
- platformforge/agents/bench.py +61 -0
- platformforge/agents/contextpack.py +137 -0
- platformforge/agents/contracts.py +394 -0
- platformforge/agents/coordinators.py +98 -0
- platformforge/agents/critic.py +113 -0
- platformforge/agents/debate.py +161 -0
- platformforge/agents/executors.py +160 -0
- platformforge/agents/guardian.py +48 -0
- platformforge/agents/mirrors.py +240 -0
- platformforge/agents/orchestrator.py +295 -0
- platformforge/agents/planner.py +116 -0
- platformforge/agents/playbook.py +51 -0
- platformforge/agents/referee.py +132 -0
- platformforge/agents/reviewers.py +113 -0
- platformforge/agents/roster.py +893 -0
- platformforge/agents/runledger.py +85 -0
- platformforge/agents/runstore.py +74 -0
- platformforge/agents/specialists.py +73 -0
- platformforge/agents/taskspec.py +94 -0
- platformforge/agents/uniqueness.py +115 -0
- platformforge/agents/verifier.py +106 -0
- platformforge/aiplat/__init__.py +4 -0
- platformforge/aiplat/models.py +175 -0
- platformforge/analytics/__init__.py +6 -0
- platformforge/analytics/capacity.py +109 -0
- platformforge/analytics/debt.py +92 -0
- platformforge/analytics/dx.py +69 -0
- platformforge/analytics/finops_v4.py +171 -0
- platformforge/analytics/goldenpath.py +131 -0
- platformforge/analytics/history.py +233 -0
- platformforge/analytics/measurement.py +149 -0
- platformforge/analytics/models.py +127 -0
- platformforge/analytics/opsanalytics.py +119 -0
- platformforge/analytics/policyintel.py +139 -0
- platformforge/analytics/reliability.py +106 -0
- platformforge/analytics/soak.py +135 -0
- platformforge/analytics/store.py +254 -0
- platformforge/bench.py +174 -0
- platformforge/cases/__init__.py +13 -0
- platformforge/cases/casefile.py +106 -0
- platformforge/cases/context_audit.py +134 -0
- platformforge/cases/ledgers.py +174 -0
- platformforge/cases/replay.py +142 -0
- platformforge/cases/routing_audit.py +162 -0
- platformforge/caveman/__init__.py +11 -0
- platformforge/caveman/compress.py +156 -0
- platformforge/cicd/__init__.py +16 -0
- platformforge/cicd/backstage.py +51 -0
- platformforge/cicd/dockerfile.py +64 -0
- platformforge/cicd/github_actions.py +122 -0
- platformforge/cicd/gitlab_ci.py +56 -0
- platformforge/cicd/gitops.py +130 -0
- platformforge/cli/__init__.py +0 -0
- platformforge/cli/fleetcmd.py +457 -0
- platformforge/cli/main.py +3832 -0
- platformforge/cloud/__init__.py +8 -0
- platformforge/cloud/aws.py +254 -0
- platformforge/cloud/common.py +69 -0
- platformforge/cloud/multicloud.py +143 -0
- platformforge/collect/__init__.py +11 -0
- platformforge/collect/detect.py +254 -0
- platformforge/context/__init__.py +10 -0
- platformforge/context/capsule.py +60 -0
- platformforge/context/gateway.py +227 -0
- platformforge/context/refs.py +49 -0
- platformforge/context/sufficiency.py +50 -0
- platformforge/core/__init__.py +1 -0
- platformforge/core/hashing.py +39 -0
- platformforge/core/ownership.py +220 -0
- platformforge/core/receipts.py +74 -0
- platformforge/core/redaction.py +97 -0
- platformforge/core/store.py +119 -0
- platformforge/core/workspace.py +89 -0
- platformforge/data/contracts/agent-handoff.schema.json +26 -0
- platformforge/data/contracts/agent-run.schema.json +27 -0
- platformforge/data/contracts/agent-spec.schema.json +43 -0
- platformforge/data/contracts/capability.schema.json +23 -0
- platformforge/data/contracts/change-event.schema.json +22 -0
- platformforge/data/contracts/change-intent.schema.json +62 -0
- platformforge/data/contracts/change-plan.schema.json +43 -0
- platformforge/data/contracts/collector-receipt.schema.json +36 -0
- platformforge/data/contracts/context-pack.schema.json +29 -0
- platformforge/data/contracts/debate.schema.json +31 -0
- platformforge/data/contracts/distribution-manifest-v1.schema.json +81 -0
- platformforge/data/contracts/drift-event.schema.json +21 -0
- platformforge/data/contracts/fact.schema.json +35 -0
- platformforge/data/contracts/finding.schema.json +20 -0
- platformforge/data/contracts/graph-v2.schema.json +67 -0
- platformforge/data/contracts/graph.schema.json +40 -0
- platformforge/data/contracts/install-receipt-v1.schema.json +49 -0
- platformforge/data/contracts/observation.schema.json +93 -0
- platformforge/data/contracts/receipt.schema.json +21 -0
- platformforge/data/contracts/recommendation.schema.json +32 -0
- platformforge/data/contracts/refusal.schema.json +16 -0
- platformforge/data/contracts/slo-contract.schema.json +25 -0
- platformforge/data/contracts/task-spec.schema.json +37 -0
- platformforge/data/contracts/workspace-manifest-v1.schema.json +62 -0
- platformforge/data/evals/cases/agent-budget-exhaustion/case.yaml +6 -0
- platformforge/data/evals/cases/agent-conflicting-specialists/case.yaml +11 -0
- platformforge/data/evals/cases/agent-fleet-optimization/case.yaml +13 -0
- platformforge/data/evals/cases/agent-host-no-subagents/case.yaml +6 -0
- platformforge/data/evals/cases/agent-incident-multi/case.yaml +13 -0
- platformforge/data/evals/cases/agent-missing-evidence/case.yaml +6 -0
- platformforge/data/evals/cases/agent-multi-domain-audit/case.yaml +14 -0
- platformforge/data/evals/cases/agent-production-change/case.yaml +15 -0
- platformforge/data/evals/cases/agent-security-mandatory/case.yaml +12 -0
- platformforge/data/evals/cases/agent-simple-iac/case.yaml +14 -0
- platformforge/data/evals/cases/approval-integrity-seal/case.yaml +6 -0
- platformforge/data/evals/cases/capacity-hot/case.yaml +7 -0
- platformforge/data/evals/cases/capacity-hot/fixture/capacity.json +5 -0
- platformforge/data/evals/cases/catalog-noowner/case.yaml +7 -0
- platformforge/data/evals/cases/catalog-noowner/fixture/catalog-info.yaml +6 -0
- platformforge/data/evals/cases/contract-fact/case.yaml +5 -0
- platformforge/data/evals/cases/contract-fact/fixture/doc.json +2 -0
- platformforge/data/evals/cases/cosign-unverified/case.yaml +8 -0
- platformforge/data/evals/cases/cosign-unverified/fixture/sig.json +33 -0
- platformforge/data/evals/cases/expected-delta-refusal/case.yaml +6 -0
- platformforge/data/evals/cases/finops-noattr/case.yaml +9 -0
- platformforge/data/evals/cases/finops-noattr/fixture/costs.json +7 -0
- platformforge/data/evals/cases/fleet-inv-absent-allocation-unknown/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-centrality-not-criticality/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-correlation-not-causality/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-dx-no-person-metrics/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-federation-restricted-denied/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-federation-secret-denied/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-history-window-deterministic/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-low-util-not-deletable/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-member-key-deterministic/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-missing-denominator/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-partial-coverage-visible/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-plan-changeintent-only/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-report-all-cited/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-small-sample-confidence/case.yaml +7 -0
- platformforge/data/evals/cases/fleet-inv-uncertain-optimization-suppressed/case.yaml +7 -0
- platformforge/data/evals/cases/gha-dangerous/case.yaml +12 -0
- platformforge/data/evals/cases/gha-dangerous/fixture/.github/workflows/ci.yaml +11 -0
- platformforge/data/evals/cases/gitops-v2/case.yaml +11 -0
- platformforge/data/evals/cases/gitops-v2/fixture/argo.yaml +41 -0
- platformforge/data/evals/cases/graph-k8s-blast/case.yaml +7 -0
- platformforge/data/evals/cases/graph-k8s-blast/fixture/deploy.yaml +15 -0
- platformforge/data/evals/cases/iac-module-nopin/case.yaml +8 -0
- platformforge/data/evals/cases/iac-module-nopin/fixture/main.tf +3 -0
- platformforge/data/evals/cases/iac-plan-replace/case.yaml +8 -0
- platformforge/data/evals/cases/iac-plan-replace/fixture/plan.json +12 -0
- platformforge/data/evals/cases/iac-sg-open/case.yaml +8 -0
- platformforge/data/evals/cases/iac-sg-open/fixture/main.tf +9 -0
- platformforge/data/evals/cases/iam-oidc/case.yaml +8 -0
- platformforge/data/evals/cases/iam-oidc/fixture/policy.json +11 -0
- platformforge/data/evals/cases/iam-wildcard-positive/case.yaml +8 -0
- platformforge/data/evals/cases/iam-wildcard-positive/fixture/policy.json +3 -0
- platformforge/data/evals/cases/k8s-bad-actor/case.yaml +19 -0
- platformforge/data/evals/cases/k8s-bad-actor/fixture/app.yaml +97 -0
- platformforge/data/evals/cases/k8s-beta-api/case.yaml +8 -0
- platformforge/data/evals/cases/k8s-beta-api/fixture/cr.yaml +7 -0
- platformforge/data/evals/cases/k8s-dep-api-boundary/case.yaml +15 -0
- platformforge/data/evals/cases/k8s-dep-api-boundary/fixture/app.yaml +25 -0
- platformforge/data/evals/cases/k8s-dep-api-clean/case.yaml +12 -0
- platformforge/data/evals/cases/k8s-dep-api-clean/fixture/app.yaml +29 -0
- platformforge/data/evals/cases/k8s-dep-api-unknown/case.yaml +11 -0
- platformforge/data/evals/cases/k8s-dep-api-unknown/fixture/app.yaml +25 -0
- platformforge/data/evals/cases/k8s-dep-api-v120/case.yaml +12 -0
- platformforge/data/evals/cases/k8s-dep-api-v120/fixture/app.yaml +25 -0
- platformforge/data/evals/cases/k8s-dep-api-v129/case.yaml +9 -0
- platformforge/data/evals/cases/k8s-dep-api-v129/fixture/app.yaml +25 -0
- platformforge/data/evals/cases/k8s-hpa-noreq/case.yaml +8 -0
- platformforge/data/evals/cases/k8s-hpa-noreq/fixture/app.yaml +28 -0
- platformforge/data/evals/cases/k8s-limits-boundary/case.yaml +10 -0
- platformforge/data/evals/cases/k8s-limits-boundary/fixture/app.yaml +15 -0
- platformforge/data/evals/cases/k8s-limits-negative/case.yaml +8 -0
- platformforge/data/evals/cases/k8s-limits-negative/fixture/deploy.yaml +15 -0
- platformforge/data/evals/cases/k8s-limits-positive/case.yaml +8 -0
- platformforge/data/evals/cases/k8s-limits-positive/fixture/deploy.yaml +14 -0
- platformforge/data/evals/cases/k8s-nopdb/case.yaml +7 -0
- platformforge/data/evals/cases/k8s-nopdb/fixture/app.yaml +34 -0
- platformforge/data/evals/cases/knowledge-registry/case.yaml +10 -0
- platformforge/data/evals/cases/kyverno-dep/case.yaml +10 -0
- platformforge/data/evals/cases/kyverno-dep/fixture/pol.yaml +18 -0
- platformforge/data/evals/cases/kyverno-legacy/case.yaml +8 -0
- platformforge/data/evals/cases/kyverno-legacy/fixture/pol.yaml +7 -0
- platformforge/data/evals/cases/live-aws-collect/case.yaml +11 -0
- platformforge/data/evals/cases/live-aws-collect/fixture/aws/ec2_describe-vpcs.json +15 -0
- platformforge/data/evals/cases/live-aws-collect/fixture/aws/iam_list-users.denied.txt +1 -0
- platformforge/data/evals/cases/live-aws-collect/fixture/aws/sts_get-caller-identity.json +5 -0
- platformforge/data/evals/cases/live-drift/case.yaml +8 -0
- platformforge/data/evals/cases/live-drift/fixture/after.json +22 -0
- platformforge/data/evals/cases/live-drift/fixture/before.json +22 -0
- platformforge/data/evals/cases/live-incident/case.yaml +7 -0
- platformforge/data/evals/cases/live-incident/fixture/changes.json +8 -0
- platformforge/data/evals/cases/live-incident/fixture/incident.json +7 -0
- platformforge/data/evals/cases/live-k8s-collect/case.yaml +10 -0
- platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_configmaps.denied.txt +1 -0
- platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_deployments.apps.json +25 -0
- platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_pods.json +17 -0
- platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_secrets.json +13 -0
- platformforge/data/evals/cases/live-multicluster/case.yaml +7 -0
- platformforge/data/evals/cases/live-multicluster/fixture/clusters.json +6 -0
- platformforge/data/evals/cases/live-plan/case.yaml +7 -0
- platformforge/data/evals/cases/live-plan/fixture/drift.json +8 -0
- platformforge/data/evals/cases/live-reconcile/case.yaml +7 -0
- platformforge/data/evals/cases/live-reconcile/fixture/desired.json +7 -0
- platformforge/data/evals/cases/live-reconcile/fixture/observed.json +18 -0
- platformforge/data/evals/cases/live-secrets-boundary/case.yaml +8 -0
- platformforge/data/evals/cases/live-secrets-boundary/fixture/envelope.json +16 -0
- platformforge/data/evals/cases/live-temporal-graph/case.yaml +10 -0
- platformforge/data/evals/cases/live-temporal-graph/fixture/graph.json +22 -0
- platformforge/data/evals/cases/ops-approval/case.yaml +6 -0
- platformforge/data/evals/cases/ops-convergence/case.yaml +6 -0
- platformforge/data/evals/cases/ops-execution-safety/case.yaml +6 -0
- platformforge/data/evals/cases/ops-intent/case.yaml +6 -0
- platformforge/data/evals/cases/ops-planning/case.yaml +6 -0
- platformforge/data/evals/cases/ops-policy/case.yaml +6 -0
- platformforge/data/evals/cases/ops-rollback/case.yaml +6 -0
- platformforge/data/evals/cases/ops-verification/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-argocd-revision/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-blind-inverse/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-git-commit/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-impossible/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-k8s-annotation-restore/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-k8s-scale/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-material-tamper/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-terraform-replan/case.yaml +6 -0
- platformforge/data/evals/cases/rollback-unknown/case.yaml +6 -0
- platformforge/data/evals/cases/routing-specialist/case.yaml +9 -0
- platformforge/data/evals/cases/rtk-roundtrip/case.yaml +5 -0
- platformforge/data/evals/cases/rtk-roundtrip/fixture/verbose.txt +300 -0
- platformforge/data/evals/cases/sbom-vuln/case.yaml +9 -0
- platformforge/data/evals/cases/sbom-vuln/fixture/sbom.json +9 -0
- platformforge/data/evals/cases/secret-prose-mixed/fixture/app.env +1 -0
- platformforge/data/evals/cases/secret-prose-mixed/fixture/policy-notes.md +7 -0
- platformforge/data/evals/cases/secrets-noleak/case.yaml +5 -0
- platformforge/data/evals/cases/secrets-noleak/fixture/app.env +2 -0
- platformforge/data/evals/cases/slo-fastburn/case.yaml +8 -0
- platformforge/data/evals/cases/slo-fastburn/fixture/events.yaml +2 -0
- platformforge/data/evals/cases/slo-fastburn/fixture/slo.yaml +4 -0
- platformforge/data/evals/cases/slo-noinput/case.yaml +8 -0
- platformforge/data/evals/cases/slo-noinput/fixture/events.yaml +1 -0
- platformforge/data/evals/cases/slo-noinput/fixture/slo.yaml +4 -0
- platformforge/data/evals/cases/sot-conflict/case.yaml +6 -0
- platformforge/data/evals/cases/token-budget/case.yaml +6 -0
- platformforge/data/evals/cases/token-budget/fixture/big.py +50 -0
- platformforge/data/evals/cases/xr-delete/case.yaml +8 -0
- platformforge/data/evals/cases/xr-delete/fixture/xr.yaml +10 -0
- platformforge/data/evals/cases/xr-graph-chain/case.yaml +8 -0
- platformforge/data/evals/cases/xr-graph-chain/fixture/xrd.yaml +90 -0
- platformforge/data/evals/cases/xr-negative-crd/case.yaml +8 -0
- platformforge/data/evals/cases/xr-negative-crd/fixture/widget.yaml +19 -0
- platformforge/data/evals/cases/xr-upbound-mr/case.yaml +8 -0
- platformforge/data/evals/cases/xr-upbound-mr/fixture/bucket.yaml +12 -0
- platformforge/data/evals/qpt-bench/change-review/case.yaml +5 -0
- platformforge/data/evals/qpt-bench/change-review/fixture/plan.json +9 -0
- platformforge/data/evals/qpt-bench/gha-security/case.yaml +5 -0
- platformforge/data/evals/qpt-bench/gha-security/fixture/ci.yml +10 -0
- platformforge/data/evals/qpt-bench/iam-investigate/case.yaml +5 -0
- platformforge/data/evals/qpt-bench/iam-investigate/fixture/policy.json +6 -0
- platformforge/data/evals/qpt-bench/incident-analysis/case.yaml +5 -0
- platformforge/data/evals/qpt-bench/incident-analysis/fixture/query.json +5 -0
- platformforge/data/evals/qpt-bench/incident-analysis/fixture/rules.yaml +6 -0
- platformforge/data/evals/qpt-bench/k8s-review/case.yaml +6 -0
- platformforge/data/evals/qpt-bench/k8s-review/fixture/deploy.yaml +15 -0
- platformforge/data/evals/qpt-bench/tf-security/case.yaml +5 -0
- platformforge/data/evals/qpt-bench/tf-security/fixture/main.tf +17 -0
- platformforge/data/knowledge/aws/eks.yaml +20 -0
- platformforge/data/knowledge/aws/iam.yaml +28 -0
- platformforge/data/knowledge/aws/network-exposure.yaml +19 -0
- platformforge/data/knowledge/aws/organizations.yaml +19 -0
- platformforge/data/knowledge/crossplane/composition-providers.yaml +20 -0
- platformforge/data/knowledge/crossplane/managed-resources.yaml +26 -0
- platformforge/data/knowledge/crossplane/v1-v2.yaml +25 -0
- platformforge/data/knowledge/kubernetes/api-deprecations.yaml +33 -0
- platformforge/data/knowledge/kubernetes/autoscaling-rbac.yaml +27 -0
- platformforge/data/knowledge/kubernetes/gateway-api.yaml +20 -0
- platformforge/data/knowledge/kubernetes/pod-security.yaml +26 -0
- platformforge/data/knowledge/sources.yaml +516 -0
- platformforge/data/knowledge/terraform/lifecycle.yaml +18 -0
- platformforge/data/knowledge/terraform/plan-semantics.yaml +26 -0
- platformforge/data/lab/chaos-pod-kill/chaos.yaml +3 -0
- platformforge/data/lab/chaos-pod-kill/facts.json +13 -0
- platformforge/data/lab/fleets/acme/capacity.yaml +35 -0
- platformforge/data/lab/fleets/acme/costs.yaml +25 -0
- platformforge/data/lab/fleets/acme/events.yaml +41 -0
- platformforge/data/lab/fleets/acme/fleet.yaml +51 -0
- platformforge/data/lab/fleets/acme/graph.yaml +45 -0
- platformforge/data/lab/fleets/acme/policies.yaml +28 -0
- platformforge/data/lab/fleets/acme/requests.yaml +24 -0
- platformforge/data/lab/scenarios/argo-bad-rollout/expected.yaml +6 -0
- platformforge/data/lab/scenarios/argo-bad-rollout/fixture/app.yaml +9 -0
- platformforge/data/lab/scenarios/aws-dump/expected.yaml +19 -0
- platformforge/data/lab/scenarios/aws-dump/fixture/compute.json +4 -0
- platformforge/data/lab/scenarios/aws-dump/fixture/iam.json +7 -0
- platformforge/data/lab/scenarios/aws-dump/fixture/network.json +6 -0
- platformforge/data/lab/scenarios/aws-dump/fixture/org.json +4 -0
- platformforge/data/lab/scenarios/aws-dump/fixture/storage.json +4 -0
- platformforge/data/lab/scenarios/container-unsigned/expected.yaml +5 -0
- platformforge/data/lab/scenarios/container-unsigned/fixture/supply.json +11 -0
- platformforge/data/lab/scenarios/cost-idle-workload/expected.yaml +5 -0
- platformforge/data/lab/scenarios/cost-idle-workload/fixture/costs.json +12 -0
- platformforge/data/lab/scenarios/crossplane-platform/expected.yaml +16 -0
- platformforge/data/lab/scenarios/crossplane-platform/fixture/negative.yaml +18 -0
- platformforge/data/lab/scenarios/crossplane-platform/fixture/xrd.yaml +90 -0
- platformforge/data/lab/scenarios/demo-platform/expected.yaml +16 -0
- platformforge/data/lab/scenarios/demo-platform/fixture/.github/workflows/ci.yml +10 -0
- platformforge/data/lab/scenarios/demo-platform/fixture/deploy.yaml +16 -0
- platformforge/data/lab/scenarios/demo-platform/fixture/infra.tf +5 -0
- platformforge/data/lab/scenarios/fleet-ai/expected.yaml +12 -0
- platformforge/data/lab/scenarios/fleet-capacity/expected.yaml +12 -0
- platformforge/data/lab/scenarios/fleet-cost/expected.yaml +18 -0
- platformforge/data/lab/scenarios/fleet-federation/expected.yaml +11 -0
- platformforge/data/lab/scenarios/fleet-golden-path/expected.yaml +18 -0
- platformforge/data/lab/scenarios/fleet-op-hotspot/expected.yaml +11 -0
- platformforge/data/lab/scenarios/fleet-ownership/expected.yaml +17 -0
- platformforge/data/lab/scenarios/fleet-partial/expected.yaml +10 -0
- platformforge/data/lab/scenarios/fleet-policy-friction/expected.yaml +10 -0
- platformforge/data/lab/scenarios/fleet-recurring-incident/expected.yaml +12 -0
- platformforge/data/lab/scenarios/fleet-report/expected.yaml +13 -0
- platformforge/data/lab/scenarios/fleet-risk/expected.yaml +18 -0
- platformforge/data/lab/scenarios/gitops-drift/expected.yaml +6 -0
- platformforge/data/lab/scenarios/gitops-drift/fixture/app.yaml +12 -0
- platformforge/data/lab/scenarios/k8s-bad-readiness/expected.yaml +6 -0
- platformforge/data/lab/scenarios/k8s-bad-readiness/fixture/deploy.yaml +23 -0
- platformforge/data/lab/scenarios/k8s-bad-readiness/fixture/netpol.yaml +9 -0
- platformforge/data/lab/scenarios/k8s-missing-limits/expected.yaml +6 -0
- platformforge/data/lab/scenarios/k8s-missing-limits/fixture/deploy.yaml +26 -0
- platformforge/data/lab/scenarios/k8s-missing-limits/fixture/netpol.yaml +9 -0
- platformforge/data/lab/scenarios/k8s-single-replica/expected.yaml +5 -0
- platformforge/data/lab/scenarios/k8s-single-replica/fixture/deploy.yaml +26 -0
- platformforge/data/lab/scenarios/k8s-single-replica/fixture/netpol.yaml +4 -0
- platformforge/data/lab/scenarios/live-aws-collect/expected.yaml +4 -0
- platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/ec2_describe-vpcs.json +15 -0
- platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/iam_list-users.denied.txt +1 -0
- platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/sts_get-caller-identity.json +5 -0
- platformforge/data/lab/scenarios/live-k8s-collect/expected.yaml +4 -0
- platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_configmaps.denied.txt +1 -0
- platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_deployments.apps.json +25 -0
- platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_pods.json +17 -0
- platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_secrets.json +13 -0
- platformforge/data/lab/scenarios/live-kind/expected.yaml +14 -0
- platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_configmaps.denied.txt +1 -0
- platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_deployments.apps.json +25 -0
- platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_pods.json +17 -0
- platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_secrets.json +13 -0
- platformforge/data/lab/scenarios/network-policy-missing/expected.yaml +6 -0
- platformforge/data/lab/scenarios/network-policy-missing/fixture/deploy.yaml +26 -0
- platformforge/data/lab/scenarios/ops-bad-rollout/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-bad-rollout/fixture/ops.yaml +33 -0
- platformforge/data/lab/scenarios/ops-break-glass/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-break-glass/fixture/ops.yaml +21 -0
- platformforge/data/lab/scenarios/ops-broken-selector/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-broken-selector/fixture/ops.yaml +21 -0
- platformforge/data/lab/scenarios/ops-changed-plan-hash/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-changed-plan-hash/fixture/ops.yaml +20 -0
- platformforge/data/lab/scenarios/ops-conflicting-operation/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-conflicting-operation/fixture/ops.yaml +21 -0
- platformforge/data/lab/scenarios/ops-expired-approval/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-expired-approval/fixture/ops.yaml +20 -0
- platformforge/data/lab/scenarios/ops-partial-failure/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-partial-failure/fixture/ops.yaml +24 -0
- platformforge/data/lab/scenarios/ops-policy-denial/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-policy-denial/fixture/ops.yaml +17 -0
- platformforge/data/lab/scenarios/ops-replica-drift/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-replica-drift/fixture/ops.yaml +28 -0
- platformforge/data/lab/scenarios/ops-rollback-annotate/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-rollback-annotate/fixture/ops.yaml +41 -0
- platformforge/data/lab/scenarios/ops-rollback-gitops/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-rollback-gitops/fixture/ops.yaml +38 -0
- platformforge/data/lab/scenarios/ops-rollback-impossible/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-rollback-impossible/fixture/ops.yaml +34 -0
- platformforge/data/lab/scenarios/ops-rollback-scale/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-rollback-scale/fixture/ops.yaml +40 -0
- platformforge/data/lab/scenarios/ops-rollback-terraform/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-rollback-terraform/fixture/ops.yaml +38 -0
- platformforge/data/lab/scenarios/ops-rollback-terraform/fixture/tf/tfplan.bin +1 -0
- platformforge/data/lab/scenarios/ops-stale-observation/expected.yaml +3 -0
- platformforge/data/lab/scenarios/ops-stale-observation/fixture/ops.yaml +20 -0
- platformforge/data/lab/scenarios/secret-exposure/expected.yaml +5 -0
- platformforge/data/lab/scenarios/secret-exposure/fixture/app.env +2 -0
- platformforge/data/lab/scenarios/slo-burn/expected.yaml +5 -0
- platformforge/data/lab/scenarios/slo-burn/fixture/events.yaml +2 -0
- platformforge/data/lab/scenarios/slo-burn/fixture/slo.yaml +4 -0
- platformforge/data/lab/scenarios/terraform-iam-wildcard/expected.yaml +5 -0
- platformforge/data/lab/scenarios/terraform-iam-wildcard/fixture/policy.json +14 -0
- platformforge/data/lab/scenarios/terraform-public-bucket/expected.yaml +6 -0
- platformforge/data/lab/scenarios/terraform-public-bucket/fixture/main.tf +5 -0
- platformforge/data/portable_skills/platformforge-agents/SKILL.md +47 -0
- platformforge/data/portable_skills/platformforge-change/SKILL.md +41 -0
- platformforge/data/portable_skills/platformforge-core/SKILL.md +62 -0
- platformforge/data/portable_skills/platformforge-economy/SKILL.md +42 -0
- platformforge/data/portable_skills/platformforge-finops/SKILL.md +34 -0
- platformforge/data/portable_skills/platformforge-fleet/SKILL.md +39 -0
- platformforge/data/portable_skills/platformforge-governance/SKILL.md +76 -0
- platformforge/data/portable_skills/platformforge-graph/SKILL.md +46 -0
- platformforge/data/portable_skills/platformforge-lab/SKILL.md +39 -0
- platformforge/data/portable_skills/platformforge-sdd/SKILL.md +39 -0
- platformforge/data/portable_skills/platformforge-security/SKILL.md +38 -0
- platformforge/data/portable_skills/platformforge-sre/SKILL.md +41 -0
- platformforge/data/rules/catalog/finops.yaml +26 -0
- platformforge/data/rules/catalog/gitops.yaml +161 -0
- platformforge/data/rules/catalog/iac.yaml +84 -0
- platformforge/data/rules/catalog/k8s.yaml +322 -0
- platformforge/data/rules/catalog/orchestration.yaml +99 -0
- platformforge/data/rules/catalog/product.yaml +42 -0
- platformforge/data/rules/catalog/routing.yaml +122 -0
- platformforge/data/rules/catalog/security.yaml +159 -0
- platformforge/data/rules/catalog/sre.yaml +67 -0
- platformforge/diagnose/__init__.py +75 -0
- platformforge/distribution/__init__.py +18 -0
- platformforge/distribution/bundle.py +127 -0
- platformforge/distribution/hostflow.py +247 -0
- platformforge/distribution/models.py +80 -0
- platformforge/distribution/service.py +225 -0
- platformforge/economy/__init__.py +27 -0
- platformforge/economy/bench.py +66 -0
- platformforge/economy/budget.py +198 -0
- platformforge/economy/cache.py +287 -0
- platformforge/economy/checkpoint.py +112 -0
- platformforge/economy/collection.py +142 -0
- platformforge/economy/doctor.py +207 -0
- platformforge/economy/engine.py +158 -0
- platformforge/economy/envelope.py +61 -0
- platformforge/economy/fleetpack.py +86 -0
- platformforge/economy/ledger.py +232 -0
- platformforge/economy/plan.py +131 -0
- platformforge/economy/pricing.py +101 -0
- platformforge/economy/qpt.py +286 -0
- platformforge/economy/reconcile.py +86 -0
- platformforge/economy/verifyplan.py +107 -0
- platformforge/economy/waste.py +123 -0
- platformforge/evals/__init__.py +16 -0
- platformforge/evals/coverage.py +140 -0
- platformforge/evals/fleet_invariants.py +260 -0
- platformforge/evals/ops_invariants.py +158 -0
- platformforge/evals/runner.py +541 -0
- platformforge/federation/__init__.py +4 -0
- platformforge/federation/node.py +135 -0
- platformforge/finops/__init__.py +14 -0
- platformforge/finops/alloc.py +31 -0
- platformforge/finops/costs.py +79 -0
- platformforge/finops/focus.py +100 -0
- platformforge/finops/graph_cost.py +51 -0
- platformforge/finops/ingest.py +144 -0
- platformforge/finops/insights.py +193 -0
- platformforge/finops/unit.py +36 -0
- platformforge/fleet/__init__.py +5 -0
- platformforge/fleet/loader.py +73 -0
- platformforge/fleet/models.py +155 -0
- platformforge/fleet/orggraph.py +172 -0
- platformforge/fleet/query.py +154 -0
- platformforge/forge/__init__.py +9 -0
- platformforge/forge/collect.py +88 -0
- platformforge/forge/delegate.py +77 -0
- platformforge/forge/manifest.py +106 -0
- platformforge/freeze/__init__.py +22 -0
- platformforge/freeze/exceptions.py +56 -0
- platformforge/freeze/manifest.py +167 -0
- platformforge/freeze/snapshots.py +108 -0
- platformforge/graph/__init__.py +39 -0
- platformforge/graph/backend.py +174 -0
- platformforge/graph/bench.py +245 -0
- platformforge/graph/build.py +130 -0
- platformforge/graph/delivery.py +183 -0
- platformforge/graph/diff.py +292 -0
- platformforge/graph/events.py +116 -0
- platformforge/graph/migrate.py +44 -0
- platformforge/graph/model.py +264 -0
- platformforge/graph/persist.py +97 -0
- platformforge/graph/query.py +290 -0
- platformforge/graph/temporal.py +78 -0
- platformforge/graph/vocab.py +63 -0
- platformforge/graphview.py +94 -0
- platformforge/iac/__init__.py +7 -0
- platformforge/iac/drift.py +53 -0
- platformforge/iac/plan.py +84 -0
- platformforge/iac/terraform.py +123 -0
- platformforge/install/__init__.py +1 -0
- platformforge/install/service.py +421 -0
- platformforge/k8s/__init__.py +8 -0
- platformforge/k8s/helm.py +152 -0
- platformforge/k8s/hubble.py +87 -0
- platformforge/k8s/manifests.py +513 -0
- platformforge/knowledge/__init__.py +1 -0
- platformforge/knowledge/packs.py +157 -0
- platformforge/knowledge/registry.py +249 -0
- platformforge/knowledge/select.py +127 -0
- platformforge/lab/__init__.py +5 -0
- platformforge/lab/chaos.py +71 -0
- platformforge/lab/fleetlab.py +351 -0
- platformforge/lab/opslab.py +271 -0
- platformforge/lab/runner.py +219 -0
- platformforge/live/__init__.py +36 -0
- platformforge/live/budget.py +92 -0
- platformforge/live/capability.py +80 -0
- platformforge/live/collectors/__init__.py +6 -0
- platformforge/live/collectors/aws.py +370 -0
- platformforge/live/collectors/aws_transport.py +98 -0
- platformforge/live/collectors/k8s.py +389 -0
- platformforge/live/collectors/k8s_transport.py +85 -0
- platformforge/live/cursors.py +48 -0
- platformforge/live/drift.py +130 -0
- platformforge/live/envelope.py +85 -0
- platformforge/live/federation.py +113 -0
- platformforge/live/fixtures.py +168 -0
- platformforge/live/identity.py +258 -0
- platformforge/live/incident.py +200 -0
- platformforge/live/models.py +435 -0
- platformforge/live/reconcile.py +382 -0
- platformforge/live/remediate.py +169 -0
- platformforge/live/store.py +256 -0
- platformforge/live/topology.py +249 -0
- platformforge/mcp/__init__.py +7 -0
- platformforge/mcp/parity.py +69 -0
- platformforge/mcp/registry.py +427 -0
- platformforge/mcp/server.py +84 -0
- platformforge/mcp/tools.py +732 -0
- platformforge/models/__init__.py +21 -0
- platformforge/models/base.py +214 -0
- platformforge/observe/__init__.py +26 -0
- platformforge/observe/capacity.py +39 -0
- platformforge/observe/dr.py +67 -0
- platformforge/observe/grafana.py +76 -0
- platformforge/observe/incident.py +58 -0
- platformforge/observe/otel.py +135 -0
- platformforge/observe/postmortem.py +72 -0
- platformforge/observe/prometheus.py +88 -0
- platformforge/observe/slo.py +125 -0
- platformforge/ops/__init__.py +8 -0
- platformforge/ops/actions.py +215 -0
- platformforge/ops/analytics.py +83 -0
- platformforge/ops/approval.py +275 -0
- platformforge/ops/autorem.py +101 -0
- platformforge/ops/config.py +175 -0
- platformforge/ops/engine.py +758 -0
- platformforge/ops/envelope.py +135 -0
- platformforge/ops/executors/__init__.py +1 -0
- platformforge/ops/executors/base.py +211 -0
- platformforge/ops/executors/git.py +77 -0
- platformforge/ops/executors/gitops.py +134 -0
- platformforge/ops/executors/terraform.py +84 -0
- platformforge/ops/gates.py +135 -0
- platformforge/ops/goldenpath.py +180 -0
- platformforge/ops/material.py +181 -0
- platformforge/ops/models.py +421 -0
- platformforge/ops/operation.py +335 -0
- platformforge/ops/opgraph.py +233 -0
- platformforge/ops/policy.py +275 -0
- platformforge/ops/preconditions.py +109 -0
- platformforge/ops/registry.py +101 -0
- platformforge/ops/risk.py +216 -0
- platformforge/ops/rollback.py +287 -0
- platformforge/ops/rollback_builders.py +163 -0
- platformforge/ops/runbook.py +269 -0
- platformforge/ops/simulate.py +125 -0
- platformforge/ops/source_of_truth.py +198 -0
- platformforge/ops/store.py +157 -0
- platformforge/ops/verify.py +190 -0
- platformforge/optimize/__init__.py +4 -0
- platformforge/optimize/engine.py +109 -0
- platformforge/optimize/models.py +68 -0
- platformforge/optimize/scan.py +196 -0
- platformforge/plan/__init__.py +107 -0
- platformforge/portable_cli.py +199 -0
- platformforge/portal_adapter.py +740 -0
- platformforge/portal_assets/chunk-7_l-b4Tr.js +1 -0
- platformforge/portal_assets/chunk-B0Iad6pD.js +1 -0
- platformforge/portal_assets/chunk-B605XnCF.js +1 -0
- platformforge/portal_assets/chunk-B7N3TXcn.js +1 -0
- platformforge/portal_assets/chunk-BAj6yGem.js +1 -0
- platformforge/portal_assets/chunk-BGUitn2r.js +1 -0
- platformforge/portal_assets/chunk-BVKnCv85.js +1 -0
- platformforge/portal_assets/chunk-BYyM9V_w.js +1 -0
- platformforge/portal_assets/chunk-BaQMhq-G.js +1 -0
- platformforge/portal_assets/chunk-BavOrxVm.js +1 -0
- platformforge/portal_assets/chunk-Bb0C24YV.js +1 -0
- platformforge/portal_assets/chunk-BfCFGPzs.js +1 -0
- platformforge/portal_assets/chunk-C2t4OUVu.js +1 -0
- platformforge/portal_assets/chunk-C3pI4QjU.js +1 -0
- platformforge/portal_assets/chunk-C4gpbtbP.js +1 -0
- platformforge/portal_assets/chunk-CGWeHhKY.js +1 -0
- platformforge/portal_assets/chunk-CJCT86Dr.js +1 -0
- platformforge/portal_assets/chunk-CXcsLDPJ.js +1 -0
- platformforge/portal_assets/chunk-CcjxoLxw.js +1 -0
- platformforge/portal_assets/chunk-D-gBPXHa.js +1 -0
- platformforge/portal_assets/chunk-D3eZogIe.js +1 -0
- platformforge/portal_assets/chunk-DQU0VyoQ.js +1 -0
- platformforge/portal_assets/chunk-DqWNkF1O.js +1 -0
- platformforge/portal_assets/chunk-DuPPkSIE.js +3 -0
- platformforge/portal_assets/chunk-DyIHOW_a.js +1 -0
- platformforge/portal_assets/chunk-Fx4zSGL5.js +1 -0
- platformforge/portal_assets/chunk-UBKt05Rg.js +1 -0
- platformforge/portal_assets/chunk-htnJDQEY.js +1 -0
- platformforge/portal_assets/chunk-kXRFMGqe.js +1 -0
- platformforge/portal_assets/chunk-qe3u763a.js +1 -0
- platformforge/portal_assets/favicon.ico +0 -0
- platformforge/portal_assets/index.html +13 -0
- platformforge/portal_assets/main-QDFVZK3T.js +5 -0
- platformforge/portal_assets/styles-MQEBDFDX.css +1 -0
- platformforge/product/__init__.py +10 -0
- platformforge/product/backstage.py +49 -0
- platformforge/product/catalog.py +74 -0
- platformforge/product/crossplane.py +341 -0
- platformforge/product/golden_paths/__init__.py +5 -0
- platformforge/product/golden_paths/engine.py +97 -0
- platformforge/product/golden_paths/library.yaml +198 -0
- platformforge/product/golden_paths/model.py +84 -0
- platformforge/product/maturity.py +107 -0
- platformforge/product/scorecards.py +84 -0
- platformforge/resources.py +36 -0
- platformforge/risk/__init__.py +5 -0
- platformforge/risk/engine.py +95 -0
- platformforge/risk/signals.py +49 -0
- platformforge/routing/__init__.py +13 -0
- platformforge/routing/decision.py +191 -0
- platformforge/routing/router.py +328 -0
- platformforge/rtk/__init__.py +9 -0
- platformforge/rtk/compact.py +127 -0
- platformforge/rtk/parsers.py +199 -0
- platformforge/rules/__init__.py +5 -0
- platformforge/rules/engine.py +345 -0
- platformforge/sandbox/__init__.py +7 -0
- platformforge/sandbox/env.py +151 -0
- platformforge/sandbox/review.py +92 -0
- platformforge/sdd/__init__.py +8 -0
- platformforge/sdd/lifecycle.py +239 -0
- platformforge/security/__init__.py +19 -0
- platformforge/security/cosign.py +67 -0
- platformforge/security/iam.py +112 -0
- platformforge/security/identity.py +84 -0
- platformforge/security/kyverno.py +106 -0
- platformforge/security/sbom.py +59 -0
- platformforge/security/scan.py +63 -0
- platformforge/security/slsa.py +86 -0
- platformforge/security/supply.py +62 -0
- platformforge/tokensave/__init__.py +20 -0
- platformforge/tokensave/budget.py +62 -0
- platformforge/tokensave/estimate.py +21 -0
- platformforge/tokensave/index.py +172 -0
- platformforge/tokensave/ledger.py +121 -0
- platformforge/tokensave/packs.py +285 -0
- platformforge/ui/__init__.py +28 -0
- platformforge/ui/app.py +465 -0
- platformforge/ui/home.py +113 -0
- platformforge/ui/i18n.py +143 -0
- platformforge/ui/kit.py +689 -0
- platformforge/ui/screen.py +628 -0
- platformforge/ui/tui.py +94 -0
- platformforge/ui/wizard.py +309 -0
- platformforge/workspace/__init__.py +13 -0
- platformforge/workspace/models.py +35 -0
- platformforge/workspace/service.py +69 -0
- platformforge-0.1.0.dist-info/METADATA +212 -0
- platformforge-0.1.0.dist-info/RECORD +659 -0
- platformforge-0.1.0.dist-info/WHEEL +4 -0
- platformforge-0.1.0.dist-info/entry_points.txt +3 -0
- platformforge-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""platform-orchestrator machinery — sealed TaskSpec → bounded DAG.
|
|
2
|
+
|
|
3
|
+
The orchestrator (§12) receives a sealed TaskSpec, inspects it, routes
|
|
4
|
+
specialists through Router V2, assembles the orchestration DAG from
|
|
5
|
+
canonical loop templates (rules/catalog/orchestration.yaml), applies
|
|
6
|
+
the run envelope, and emits a dispatch plan + run record. It is
|
|
7
|
+
deterministic: the model tier on its AgentSpec describes the host's
|
|
8
|
+
reasoning budget, not a code path here.
|
|
9
|
+
|
|
10
|
+
It never (§13): analyzes domains itself, approves mutations, executes
|
|
11
|
+
arbitrary actions, invents evidence, declares itself done, or verifies
|
|
12
|
+
its own run — `verify` is always a separate stage run by
|
|
13
|
+
platform-verifier.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
import yaml
|
|
23
|
+
|
|
24
|
+
from platformforge.agents.contracts import (
|
|
25
|
+
AgentRefusal,
|
|
26
|
+
AgentRunEnvelope,
|
|
27
|
+
AgentRunRecord,
|
|
28
|
+
PlatformTaskSpec,
|
|
29
|
+
envelope_for,
|
|
30
|
+
refusal,
|
|
31
|
+
)
|
|
32
|
+
from platformforge.agents.roster import AGENTS, resolve
|
|
33
|
+
from platformforge.agents.taskspec import require_sealed
|
|
34
|
+
from platformforge.models.base import stable_id
|
|
35
|
+
from platformforge.resources import data_path
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class DagNode:
|
|
40
|
+
stage: str
|
|
41
|
+
agent: str
|
|
42
|
+
depends_on: tuple[str, ...] = ()
|
|
43
|
+
conditional: str = "" # "" | conflicts | accepted
|
|
44
|
+
external: bool = False # human-gate — outside the runtime
|
|
45
|
+
parallel_group: int = 0
|
|
46
|
+
|
|
47
|
+
def to_dict(self) -> dict[str, Any]:
|
|
48
|
+
return {"stage": self.stage, "agent": self.agent,
|
|
49
|
+
"depends_on": list(self.depends_on),
|
|
50
|
+
"conditional": self.conditional,
|
|
51
|
+
"external": self.external,
|
|
52
|
+
"parallel_group": self.parallel_group}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class OrchestrationPlan:
|
|
57
|
+
"""The orchestrator's output: a DAG + envelope + run skeleton."""
|
|
58
|
+
dag: list[DagNode]
|
|
59
|
+
envelope: AgentRunEnvelope
|
|
60
|
+
coordinator: str
|
|
61
|
+
run: AgentRunRecord
|
|
62
|
+
mode: str = "coordinated"
|
|
63
|
+
fallbacks: tuple[str, ...] = ()
|
|
64
|
+
refusal_doc: dict[str, Any] | None = None
|
|
65
|
+
|
|
66
|
+
def topological_stages(self) -> list[list[DagNode]]:
|
|
67
|
+
"""Stage groups — same list may run in parallel (§79)."""
|
|
68
|
+
done: set[str] = set()
|
|
69
|
+
groups: list[list[DagNode]] = []
|
|
70
|
+
remaining = {n.stage: n for n in self.dag}
|
|
71
|
+
while remaining:
|
|
72
|
+
ready = [n for n in remaining.values()
|
|
73
|
+
if all(d in done for d in n.depends_on)]
|
|
74
|
+
if not ready:
|
|
75
|
+
break # cycle guard — surfaced by validate_dag upstream
|
|
76
|
+
groups.append(sorted(ready, key=lambda n: n.stage))
|
|
77
|
+
for n in ready:
|
|
78
|
+
done.add(n.stage)
|
|
79
|
+
del remaining[n.stage]
|
|
80
|
+
return groups
|
|
81
|
+
|
|
82
|
+
def to_dict(self) -> dict[str, Any]:
|
|
83
|
+
return {"coordinator": self.coordinator, "mode": self.mode,
|
|
84
|
+
"dag": [n.to_dict() for n in self.dag],
|
|
85
|
+
"stages": [[n.stage for n in g]
|
|
86
|
+
for g in self.topological_stages()],
|
|
87
|
+
"envelope": self.envelope.to_dict(),
|
|
88
|
+
"run": self.run.to_dict(),
|
|
89
|
+
"fallbacks": list(self.fallbacks),
|
|
90
|
+
"refusal": self.refusal_doc}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def load_loops(path: str | Path | None = None) -> dict[str, Any]:
|
|
94
|
+
p = Path(path) if path else data_path("rules", "catalog",
|
|
95
|
+
"orchestration.yaml")
|
|
96
|
+
doc = yaml.safe_load(p.read_text()) or {}
|
|
97
|
+
return doc.get("loops", {}) or {}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _specialists_for(domains: tuple[str, ...]) -> list[str]:
|
|
101
|
+
"""Routed specialists for the task's domains — must exist."""
|
|
102
|
+
out = []
|
|
103
|
+
for d in domains:
|
|
104
|
+
for a in AGENTS.values():
|
|
105
|
+
if a.role == "specialist" and d in a.domains:
|
|
106
|
+
out.append(a.name)
|
|
107
|
+
break
|
|
108
|
+
return out
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def build_dag(loop: dict[str, Any], spec: PlatformTaskSpec,
|
|
112
|
+
specialists: list[str] | None = None) \
|
|
113
|
+
-> tuple[list[DagNode], list[str]]:
|
|
114
|
+
"""Instantiate a loop template. `fanout:` stages expand to one node
|
|
115
|
+
per routed specialist; a later `after: [fanout-id]` waits for ALL of
|
|
116
|
+
them (§80). Returns (nodes, missing_agent_refs)."""
|
|
117
|
+
specs = loop.get("stages", [])
|
|
118
|
+
fanout = specialists if specialists is not None \
|
|
119
|
+
else _specialists_for(spec.domains)
|
|
120
|
+
if fanout == []:
|
|
121
|
+
fanout = ["platform-orchestrator"]
|
|
122
|
+
|
|
123
|
+
# pass 1 — instantiate nodes (fanout stages fan out per specialist)
|
|
124
|
+
instances: list[tuple[str, DagNode]] = []
|
|
125
|
+
group = 0
|
|
126
|
+
for st in specs:
|
|
127
|
+
if st.get("fanout"):
|
|
128
|
+
group += 1
|
|
129
|
+
for name in fanout:
|
|
130
|
+
instances.append((st["id"], DagNode(
|
|
131
|
+
stage=f"{st['id']}:{name}", agent=name,
|
|
132
|
+
parallel_group=group,
|
|
133
|
+
conditional=st.get("if", ""))))
|
|
134
|
+
continue
|
|
135
|
+
agent = st.get("agent")
|
|
136
|
+
external = bool(st.get("external")) or agent == "human-gate"
|
|
137
|
+
instances.append((st["id"], DagNode(
|
|
138
|
+
stage=st["id"], agent=agent or "?", external=external,
|
|
139
|
+
conditional=st.get("if", ""))))
|
|
140
|
+
|
|
141
|
+
# pass 2 — resolve `after` template ids to concrete stage names
|
|
142
|
+
by_tid: dict[str, list[str]] = {}
|
|
143
|
+
for tid, n in instances:
|
|
144
|
+
by_tid.setdefault(tid, []).append(n.stage)
|
|
145
|
+
missing: list[str] = []
|
|
146
|
+
for st, (tid, n) in zip(
|
|
147
|
+
[s for s in specs for _ in
|
|
148
|
+
([s] if not s.get("fanout") else fanout)],
|
|
149
|
+
instances):
|
|
150
|
+
deps = tuple(d for tid_dep in st.get("after", [])
|
|
151
|
+
for d in by_tid.get(tid_dep, []))
|
|
152
|
+
n.depends_on = deps
|
|
153
|
+
if n.agent != "human-gate" and not n.external \
|
|
154
|
+
and resolve(n.agent) is None:
|
|
155
|
+
missing.append(n.agent)
|
|
156
|
+
return [n for _, n in instances], missing
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def validate_dag(nodes: list[DagNode]) -> list[str]:
|
|
160
|
+
"""Structural checks — every dep exists, no self-deps, verifier last."""
|
|
161
|
+
errs = []
|
|
162
|
+
ids = {n.stage for n in nodes}
|
|
163
|
+
for n in nodes:
|
|
164
|
+
for d in n.depends_on:
|
|
165
|
+
if d not in ids:
|
|
166
|
+
errs.append(f"{n.stage} depends on missing {d}")
|
|
167
|
+
if n.stage in n.depends_on:
|
|
168
|
+
errs.append(f"{n.stage} depends on itself")
|
|
169
|
+
verifiers = [n for n in nodes
|
|
170
|
+
if resolve(n.agent) and resolve(n.agent).role
|
|
171
|
+
== "verifier"]
|
|
172
|
+
for v in verifiers:
|
|
173
|
+
downstream = [n for n in nodes if v.stage in n.depends_on]
|
|
174
|
+
if downstream:
|
|
175
|
+
errs.append("verifier is not terminal — stages "
|
|
176
|
+
+ ", ".join(n.stage for n in downstream)
|
|
177
|
+
+ " depend on it")
|
|
178
|
+
return errs
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def prepare(spec: PlatformTaskSpec, *, loop_name: str = "platform-audit",
|
|
182
|
+
router_decision: dict[str, Any] | None = None,
|
|
183
|
+
specialists: list[str] | None = None,
|
|
184
|
+
loops: dict[str, Any] | None = None) -> OrchestrationPlan:
|
|
185
|
+
"""§12 — sealed spec in, dispatch plan out. Deterministic.
|
|
186
|
+
|
|
187
|
+
Refuses: unsealed complex spec, unknown loop, missing agents,
|
|
188
|
+
structurally invalid DAG. `loops` overrides the catalog —
|
|
189
|
+
used by tests and embedders; the CLI always uses the catalog."""
|
|
190
|
+
complex_ = spec.complexity != "low" or spec.risk in _GOVERN_NONE
|
|
191
|
+
# polish §24/§28 — a supplied router_decision must be the canonical
|
|
192
|
+
# platformforge/routing-decision/v1 emitted by routing.decide().
|
|
193
|
+
# EconomyEngine advice (economy-advice/v1) is refused here — it may
|
|
194
|
+
# inform the signal, never activate a route.
|
|
195
|
+
if router_decision:
|
|
196
|
+
from platformforge.routing.decision import SCHEMA_DEC
|
|
197
|
+
if router_decision.get("schema") != SCHEMA_DEC:
|
|
198
|
+
run = _record(spec, dict(router_decision),
|
|
199
|
+
verdict="unresolved")
|
|
200
|
+
return OrchestrationPlan(
|
|
201
|
+
dag=[], envelope=envelope_for(spec.budget),
|
|
202
|
+
coordinator="", run=run,
|
|
203
|
+
refusal_doc=refusal(
|
|
204
|
+
AgentRefusal.ROUTE_UNRESOLVED,
|
|
205
|
+
"router_decision is not a canonical "
|
|
206
|
+
"platformforge/routing-decision/v1 — advice, raw "
|
|
207
|
+
"dicts and foreign schemas cannot activate a route",
|
|
208
|
+
"produce the decision via "
|
|
209
|
+
"platformforge.routing.decision.decide()"))
|
|
210
|
+
unsealed = require_sealed(spec, complex_=complex_)
|
|
211
|
+
if unsealed:
|
|
212
|
+
run = _record(spec, router_decision or {}, verdict="unresolved")
|
|
213
|
+
return OrchestrationPlan(dag=[], envelope=envelope_for(
|
|
214
|
+
spec.budget), coordinator="", run=run,
|
|
215
|
+
refusal_doc=unsealed)
|
|
216
|
+
loops = load_loops() if loops is None else loops
|
|
217
|
+
loop = loops.get(loop_name)
|
|
218
|
+
if loop is None:
|
|
219
|
+
run = _record(spec, router_decision or {}, verdict="unresolved")
|
|
220
|
+
return OrchestrationPlan(
|
|
221
|
+
dag=[], envelope=envelope_for(spec.budget), coordinator="",
|
|
222
|
+
run=run,
|
|
223
|
+
refusal_doc=refusal(
|
|
224
|
+
AgentRefusal.ROUTE_UNRESOLVED,
|
|
225
|
+
f"no orchestration loop named {loop_name!r}",
|
|
226
|
+
"pick one of: " + ", ".join(sorted(loops))))
|
|
227
|
+
nodes, missing = build_dag(loop, spec, specialists=specialists)
|
|
228
|
+
errs = validate_dag(nodes)
|
|
229
|
+
env = envelope_for(spec.budget)
|
|
230
|
+
coordinator = loop.get("coordinator") or ""
|
|
231
|
+
run = _record(spec, router_decision or {}, verdict="unresolved")
|
|
232
|
+
if missing or errs:
|
|
233
|
+
run = _record(spec, router_decision or {}, verdict="unresolved")
|
|
234
|
+
return OrchestrationPlan(
|
|
235
|
+
dag=nodes, envelope=env, coordinator=coordinator, run=run,
|
|
236
|
+
refusal_doc=refusal(
|
|
237
|
+
AgentRefusal.ROUTE_UNRESOLVED,
|
|
238
|
+
f"loop {loop_name!r} references missing agents "
|
|
239
|
+
f"{missing} or has DAG errors {errs}",
|
|
240
|
+
"fix the loop template or the roster — never skip "
|
|
241
|
+
"the verifier stage"))
|
|
242
|
+
run.agents = tuple(sorted({n.agent for n in nodes if not n.external}))
|
|
243
|
+
run.gates = tuple(n.stage for n in nodes if n.external)
|
|
244
|
+
# parallel fanout charged against the envelope up front (§113)
|
|
245
|
+
fanout = max((n.parallel_group for n in nodes), default=0)
|
|
246
|
+
env.charge(agents=len(run.agents), fanout=max(fanout - 1, 0))
|
|
247
|
+
over = env.check()
|
|
248
|
+
if over:
|
|
249
|
+
return OrchestrationPlan(dag=nodes, envelope=env,
|
|
250
|
+
coordinator=coordinator, run=run,
|
|
251
|
+
refusal_doc=over)
|
|
252
|
+
return OrchestrationPlan(dag=nodes, envelope=env,
|
|
253
|
+
coordinator=coordinator, run=run,
|
|
254
|
+
mode="coordinated",
|
|
255
|
+
fallbacks=("playbook",))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
_GOVERN_NONE = ("high", "critical")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _record(spec: PlatformTaskSpec, router: dict[str, Any],
|
|
262
|
+
verdict: str) -> AgentRunRecord:
|
|
263
|
+
rid = stable_id("PF-RUN", spec.task_id, spec.spec_hash)
|
|
264
|
+
return AgentRunRecord(run_id=rid, task_spec_hash=spec.spec_hash,
|
|
265
|
+
router_decision=router, verdict=verdict)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def checkpoint(record: AgentRunRecord,
|
|
269
|
+
done_stages: tuple[str, ...]) -> dict[str, Any]:
|
|
270
|
+
"""§140–141 — resumable state preserving spent budget and evidence."""
|
|
271
|
+
return {"run_id": record.run_id,
|
|
272
|
+
"task_spec_hash": record.task_spec_hash,
|
|
273
|
+
"done": list(done_stages),
|
|
274
|
+
"budget_spent": record.budget,
|
|
275
|
+
"evidence": list(record.evidence),
|
|
276
|
+
"verdict": record.verdict}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def resume(record: AgentRunRecord, checkpoint: dict[str, Any],
|
|
280
|
+
spec: PlatformTaskSpec) -> dict[str, Any] | AgentRunRecord:
|
|
281
|
+
"""§142 — resume revalidates before continuing; budget never resets."""
|
|
282
|
+
if checkpoint.get("task_spec_hash") != spec.spec_hash:
|
|
283
|
+
return refusal(
|
|
284
|
+
AgentRefusal.SPEC_UNSEALED,
|
|
285
|
+
"checkpoint spec hash differs from the current spec — the "
|
|
286
|
+
"task changed under the run",
|
|
287
|
+
"discard the checkpoint and start a new run")
|
|
288
|
+
if checkpoint.get("run_id") != record.run_id:
|
|
289
|
+
return refusal(
|
|
290
|
+
AgentRefusal.ROUTE_UNRESOLVED,
|
|
291
|
+
"checkpoint run_id does not match the record",
|
|
292
|
+
"resume with the checkpoint emitted by the same run")
|
|
293
|
+
record.checkpoint = dict(checkpoint)
|
|
294
|
+
record.verdict = "candidate" # resumed work is unverified until close
|
|
295
|
+
return record
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""platform-planner — turn a user goal into a TaskSpec (§14).
|
|
2
|
+
|
|
3
|
+
Deterministic intake: domain inference is a declared keyword table, not
|
|
4
|
+
a model call. The planner emits a *draft* spec — it never executes,
|
|
5
|
+
never approves, never verifies (§15).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from platformforge.agents.contracts import PlatformTaskSpec
|
|
11
|
+
from platformforge.agents.taskspec import MUTATING_CAPS
|
|
12
|
+
from platformforge.models.base import stable_id
|
|
13
|
+
|
|
14
|
+
# intent keyword → domain. Declared data: an operator can audit why a
|
|
15
|
+
# domain was inferred; nothing is hidden in a prompt.
|
|
16
|
+
_DOMAIN_KEYWORDS: dict[str, tuple[str, ...]] = {
|
|
17
|
+
"iac": ("terraform", "hcl", "tofu", "plan", "state", "iac",
|
|
18
|
+
"module"),
|
|
19
|
+
"k8s": ("kubernetes", "k8s", "manifest", "helm", "kustomize",
|
|
20
|
+
"deployment", "pod", "cluster", "workload"),
|
|
21
|
+
"gitops": ("argocd", "flux", "gitops", "rollout"),
|
|
22
|
+
"aws": ("aws", "iam", "s3", "ec2", "vpc", "eks", "rds"),
|
|
23
|
+
"crossplane": ("crossplane", "xrd", "composition", "claim"),
|
|
24
|
+
"sre": ("slo", "sli", "incident", "latency", "burn", "otel",
|
|
25
|
+
"reliability", "capacity", "outage"),
|
|
26
|
+
"security": ("security", "secret", "sbom", "supply", "cve",
|
|
27
|
+
"signature", "slsa", "exposure", "vulnerability"),
|
|
28
|
+
"finops": ("cost", "finops", "billing", "spend", "savings",
|
|
29
|
+
"rightsizing", "focus"),
|
|
30
|
+
"graph": ("dependency", "blast", "impact", "graph", "path"),
|
|
31
|
+
"fleet": ("fleet", "clusters", "accounts", "org", "portfolio"),
|
|
32
|
+
"policy": ("policy", "opa", "kyverno", "cel", "governance",
|
|
33
|
+
"compliance"),
|
|
34
|
+
"product": ("golden", "self-service", "dx", "adoption",
|
|
35
|
+
"developer", "maturity"),
|
|
36
|
+
"ai": ("gpu", "mig", "model serving", "ai platform", "llm infra"),
|
|
37
|
+
"federation": ("federation", "forgenode", "sibling forge",
|
|
38
|
+
"export policy"),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_SCOPE_KEYWORDS = {
|
|
42
|
+
"fleet": ("fleet", "organization", "org-wide", "all clusters",
|
|
43
|
+
"all accounts"),
|
|
44
|
+
"runtime": ("incident", "running", "live", "production", "slo"),
|
|
45
|
+
"change": ("change", "migrate", "upgrade", "deploy", "apply",
|
|
46
|
+
"rollout"),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_EVIDENCE_BY_DOMAIN = {
|
|
50
|
+
"iac": ("iac artifacts", "plan facts", "state facts"),
|
|
51
|
+
"k8s": ("manifest facts", "coverage"),
|
|
52
|
+
"aws": ("cloud inventory facts", "coverage"),
|
|
53
|
+
"sre": ("telemetry facts", "freshness"),
|
|
54
|
+
"security": ("security facts", "sbom/material"),
|
|
55
|
+
"finops": ("cost facts", "denominators"),
|
|
56
|
+
"fleet": ("member observations", "coverage"),
|
|
57
|
+
"graph": ("graph with provenance",),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def infer_domains(intent: str) -> tuple[str, ...]:
|
|
62
|
+
terms = intent.lower()
|
|
63
|
+
out = [d for d, kws in _DOMAIN_KEYWORDS.items()
|
|
64
|
+
if any(k in terms for k in kws)]
|
|
65
|
+
return tuple(sorted(out))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def infer_scope(intent: str) -> str:
|
|
69
|
+
terms = intent.lower()
|
|
70
|
+
for scope, kws in _SCOPE_KEYWORDS.items():
|
|
71
|
+
if any(k in terms for k in kws):
|
|
72
|
+
return scope
|
|
73
|
+
return "repo"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def infer_complexity(intent: str, domains: tuple[str, ...],
|
|
77
|
+
scope: str) -> str:
|
|
78
|
+
terms = intent.lower()
|
|
79
|
+
if scope == "fleet" or len(domains) >= 3 or \
|
|
80
|
+
any(w in terms for w in ("entire", "whole", "audit",
|
|
81
|
+
"across")):
|
|
82
|
+
return "high"
|
|
83
|
+
if len(domains) == 2 or scope in ("runtime", "change"):
|
|
84
|
+
return "medium"
|
|
85
|
+
return "low"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def plan(intent: str, *, domains: tuple[str, ...] | None = None,
|
|
89
|
+
risk: str = "low", complexity: str | None = None,
|
|
90
|
+
autonomy: str = "read-only",
|
|
91
|
+
acceptance_criteria: tuple[str, ...] | None = None,
|
|
92
|
+
rollback_requirement: str = "none") -> PlatformTaskSpec:
|
|
93
|
+
"""intent → draft PlatformTaskSpec. Caller reviews + seals it."""
|
|
94
|
+
doms = domains or infer_domains(intent)
|
|
95
|
+
scope = infer_scope(intent)
|
|
96
|
+
cx = complexity or infer_complexity(intent, doms, scope)
|
|
97
|
+
if risk == "low" and scope == "change":
|
|
98
|
+
risk = "high"
|
|
99
|
+
ev = sorted({e for d in doms
|
|
100
|
+
for e in _EVIDENCE_BY_DOMAIN.get(d, ("facts",))}
|
|
101
|
+
| {"coverage", "freshness"})
|
|
102
|
+
budget = {"low": "small", "medium": "standard",
|
|
103
|
+
"high": "deep"}.get(cx, "standard")
|
|
104
|
+
if risk in ("high", "critical"):
|
|
105
|
+
budget = "critical" if risk == "critical" else "deep"
|
|
106
|
+
spec = PlatformTaskSpec(
|
|
107
|
+
task_id=stable_id("PF-TASK", intent, ",".join(doms), scope),
|
|
108
|
+
intent=intent, scope=scope, domains=doms, risk=risk,
|
|
109
|
+
complexity=cx, evidence_requirements=tuple(ev),
|
|
110
|
+
acceptance_criteria=acceptance_criteria
|
|
111
|
+
or (("every domain answer carries fact_ids or a named "
|
|
112
|
+
"unresolved"), "independent verifier closes the run"),
|
|
113
|
+
autonomy=autonomy,
|
|
114
|
+
forbidden_capabilities=MUTATING_CAPS,
|
|
115
|
+
rollback_requirement=rollback_requirement, budget=budget)
|
|
116
|
+
return spec
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Playbook — coordinator decomposition as sequential steps.
|
|
2
|
+
|
|
3
|
+
This is the zero-subagent floor (§166–170): a host without native agent
|
|
4
|
+
delegation runs the same plan as explicit phases, with identical
|
|
5
|
+
evidence requirements. Nothing about the run becomes unverifiable when
|
|
6
|
+
fan-out is unavailable — the orchestrator's DAG degrades to a list.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from platformforge.agents.contracts import AgentRefusal, refusal
|
|
14
|
+
from platformforge.agents.roster import AGENTS, coordinator_for, resolve
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def playbook(coordinator: str = "platform-orchestrator",
|
|
18
|
+
domain: str | None = None) -> dict[str, Any]:
|
|
19
|
+
if domain and coordinator == "platform-orchestrator":
|
|
20
|
+
spec = coordinator_for(domain) or AGENTS[coordinator]
|
|
21
|
+
else:
|
|
22
|
+
spec = resolve(coordinator)
|
|
23
|
+
if spec is None:
|
|
24
|
+
return refusal(
|
|
25
|
+
AgentRefusal.UNKNOWN_AGENT,
|
|
26
|
+
f"{coordinator!r} is not in the canonical roster",
|
|
27
|
+
"agents list — pick a name that exists")
|
|
28
|
+
steps = [
|
|
29
|
+
{"step": 1, "verb": "inspect", "why": "inventory artifacts"},
|
|
30
|
+
{"step": 2, "verb": f"analyze ({', '.join(spec.domains)})",
|
|
31
|
+
"why": "facts over the domain artifacts"},
|
|
32
|
+
{"step": 3, "verb": "judge", "why": "rules over facts → findings"},
|
|
33
|
+
{"step": 4, "verb": "graph build + gaps",
|
|
34
|
+
"why": "wire provenance, surface structural gaps"},
|
|
35
|
+
{"step": 5, "verb": "economy",
|
|
36
|
+
"why": "report context spend — never claim unmeasured savings"},
|
|
37
|
+
]
|
|
38
|
+
if spec.role in ("coordinator", "orchestrator"):
|
|
39
|
+
steps.insert(1, {"step": 1.5, "verb": "route",
|
|
40
|
+
"why": "pick specialists via routing signals"})
|
|
41
|
+
steps[0]["step"] = 1
|
|
42
|
+
return {"agent": spec.name, "role": spec.role,
|
|
43
|
+
"mode": "playbook",
|
|
44
|
+
"steps": steps, "delegates_to": list(spec.delegates_to),
|
|
45
|
+
"access": spec.access,
|
|
46
|
+
"contract": {"when": spec.when_to_enter,
|
|
47
|
+
"when_not_to_enter": spec.when_not_to_enter,
|
|
48
|
+
"never": spec.never,
|
|
49
|
+
"required_evidence": list(spec.required_evidence),
|
|
50
|
+
"verifier": spec.verifier,
|
|
51
|
+
"outputs": list(spec.outputs)}}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""platform-debate-referee — deterministic adjudication (cycle5.1 §24–26).
|
|
2
|
+
|
|
3
|
+
Positions are scored on ten declared axes, never averaged into mush:
|
|
4
|
+
|
|
5
|
+
tier evidence tier (lower wins)
|
|
6
|
+
freshness source/claim freshness (current|fresh|stale|...)
|
|
7
|
+
version_compat does the claim's version basis match context
|
|
8
|
+
scope does the claim cover the asked scope
|
|
9
|
+
contradictory count of unresolved contradictions against it
|
|
10
|
+
completeness fraction of required evidence fields present
|
|
11
|
+
coverage fraction of asked scope the evidence covers
|
|
12
|
+
runtime_alignment claim consistent with runtime evidence
|
|
13
|
+
risk accepting a risky claim needs stronger evidence
|
|
14
|
+
historical_support claim supported by history patterns
|
|
15
|
+
|
|
16
|
+
A position with no evidence loses regardless of prose confidence. Ties
|
|
17
|
+
and gaps are explicit in the receipt; the referee never averages
|
|
18
|
+
positions into a compromise (§26).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from platformforge.models.base import stable_id
|
|
26
|
+
|
|
27
|
+
_FRESH = {"current": 0, "fresh": 1, "stale": 3,
|
|
28
|
+
"deprecated": 5, "superseded": 5, "conflicted": 4,
|
|
29
|
+
"unresolved": 6}
|
|
30
|
+
|
|
31
|
+
# weight per axis — evidence still dominates, but the §25 axes all move
|
|
32
|
+
# the score (§115 + cycle5.1 expansion: coverage, runtime alignment,
|
|
33
|
+
# risk, historical support)
|
|
34
|
+
_W = {"tier": 10.0, "freshness": 3.0, "version_compat": 4.0,
|
|
35
|
+
"scope": 3.0, "contradictory": 8.0, "completeness": 4.0,
|
|
36
|
+
"coverage": 4.0, "runtime_alignment": 4.0, "risk": 6.0,
|
|
37
|
+
"historical_support": 2.0}
|
|
38
|
+
|
|
39
|
+
_REQUIRED_EVIDENCE_FIELDS = ("fact_id", "kind", "tier")
|
|
40
|
+
|
|
41
|
+
_RISK = {"low": 0.0, "medium": 1.0, "high": 2.0, "critical": 3.0}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _tri(value: Any) -> float:
|
|
45
|
+
"""tri-state: True→1.0, False→0.0, unknown/absent→0.5."""
|
|
46
|
+
return 1.0 if value is True else 0.0 if value is False else 0.5
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _completeness(p: dict[str, Any]) -> float:
|
|
50
|
+
"""Fraction of positions' evidence refs that resolve to full facts."""
|
|
51
|
+
ev = p.get("evidence_facts") or []
|
|
52
|
+
if not ev:
|
|
53
|
+
return 0.0
|
|
54
|
+
full = sum(1 for e in ev
|
|
55
|
+
if isinstance(e, dict)
|
|
56
|
+
and all(e.get(k) is not None
|
|
57
|
+
for k in _REQUIRED_EVIDENCE_FIELDS))
|
|
58
|
+
return full / len(ev)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _score(p: dict[str, Any]) -> dict[str, Any]:
|
|
62
|
+
tier = p.get("evidence_tier", 7)
|
|
63
|
+
try:
|
|
64
|
+
tier = min(max(int(tier), 0), 7)
|
|
65
|
+
except (TypeError, ValueError):
|
|
66
|
+
tier = 7
|
|
67
|
+
freshness = _FRESH.get(str(p.get("freshness", "unresolved")).lower(), 6)
|
|
68
|
+
vc = _tri(p.get("version_compat"))
|
|
69
|
+
scope = _tri(p.get("scope_match"))
|
|
70
|
+
contradictions = len(p.get("contradictions") or [])
|
|
71
|
+
completeness = _completeness(p)
|
|
72
|
+
coverage = p.get("coverage")
|
|
73
|
+
try:
|
|
74
|
+
coverage = min(max(float(coverage), 0.0), 1.0)
|
|
75
|
+
except (TypeError, ValueError):
|
|
76
|
+
coverage = 0.0
|
|
77
|
+
runtime = _tri(p.get("runtime_alignment"))
|
|
78
|
+
risk = _RISK.get(str(p.get("risk", "low")).lower(), 0.0)
|
|
79
|
+
history = _tri(p.get("historical_support"))
|
|
80
|
+
|
|
81
|
+
ev = p.get("evidence_fact_ids") or p.get("evidence") or []
|
|
82
|
+
has_ev = bool(ev) or completeness > 0
|
|
83
|
+
score = (100.0 if has_ev else 0.0)
|
|
84
|
+
score -= _W["tier"] * tier
|
|
85
|
+
score -= _W["freshness"] * freshness
|
|
86
|
+
score += _W["version_compat"] * vc
|
|
87
|
+
score += _W["scope"] * scope
|
|
88
|
+
score -= _W["contradictory"] * min(contradictions, 3)
|
|
89
|
+
score += _W["completeness"] * completeness
|
|
90
|
+
score += _W["coverage"] * coverage
|
|
91
|
+
score += _W["runtime_alignment"] * runtime
|
|
92
|
+
# risk penalizes a weak-evidence position, not a cautious one
|
|
93
|
+
score -= _W["risk"] * risk * (1.0 if tier >= 4 else 0.25)
|
|
94
|
+
score += _W["historical_support"] * history
|
|
95
|
+
return {"agent": p.get("agent", "?"), "claim": p.get("claim", ""),
|
|
96
|
+
"tier": tier, "freshness": freshness,
|
|
97
|
+
"version_compat": vc, "scope": scope,
|
|
98
|
+
"contradictions": contradictions,
|
|
99
|
+
"completeness": round(completeness, 3),
|
|
100
|
+
"coverage": round(coverage, 3),
|
|
101
|
+
"runtime_alignment": runtime,
|
|
102
|
+
"risk": p.get("risk", "low"),
|
|
103
|
+
"historical_support": history,
|
|
104
|
+
"evidence_count": len(ev), "score": round(score, 3),
|
|
105
|
+
"evidence": ev}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def referee(positions: list[dict[str, Any]]) -> dict[str, Any]:
|
|
109
|
+
"""positions: [{agent, claim, evidence_fact_ids|evidence_facts,
|
|
110
|
+
evidence_tier, freshness, version_compat, scope_match, coverage,
|
|
111
|
+
runtime_alignment, risk, historical_support, contradictions, ...}]
|
|
112
|
+
→ winner + receipt. Ties/gaps explicit."""
|
|
113
|
+
scored = [_score(p) for p in positions]
|
|
114
|
+
scored.sort(key=lambda s: -s["score"])
|
|
115
|
+
winner = scored[0] if scored else None
|
|
116
|
+
tied = [s for s in scored if winner and s["score"] == winner["score"]]
|
|
117
|
+
winner_has_ev = bool(winner and (winner["evidence"]
|
|
118
|
+
or winner["completeness"] > 0))
|
|
119
|
+
return {
|
|
120
|
+
"winner": winner,
|
|
121
|
+
"tied": len(tied) > 1,
|
|
122
|
+
"positions": scored,
|
|
123
|
+
"unresolved": winner is None or not winner_has_ev,
|
|
124
|
+
"receipt": {"refusal_code": None if winner_has_ev
|
|
125
|
+
else "platform.evidence.unresolved",
|
|
126
|
+
"receipt_id": stable_id(
|
|
127
|
+
"PF-REF", str(len(positions)),
|
|
128
|
+
",".join(sorted(p.get("agent", "?")
|
|
129
|
+
for p in positions)))},
|
|
130
|
+
"axes": sorted(_W),
|
|
131
|
+
"note": "ten declared axes decide; no position is averaged away",
|
|
132
|
+
}
|