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.
Files changed (659) hide show
  1. platformforge/__init__.py +7 -0
  2. platformforge/__main__.py +5 -0
  3. platformforge/_forge_portal.py +913 -0
  4. platformforge/_graphstudio.py +376 -0
  5. platformforge/_graphview.py +181 -0
  6. platformforge/_installkit.py +1706 -0
  7. platformforge/agents/__init__.py +156 -0
  8. platformforge/agents/bench.py +61 -0
  9. platformforge/agents/contextpack.py +137 -0
  10. platformforge/agents/contracts.py +394 -0
  11. platformforge/agents/coordinators.py +98 -0
  12. platformforge/agents/critic.py +113 -0
  13. platformforge/agents/debate.py +161 -0
  14. platformforge/agents/executors.py +160 -0
  15. platformforge/agents/guardian.py +48 -0
  16. platformforge/agents/mirrors.py +240 -0
  17. platformforge/agents/orchestrator.py +295 -0
  18. platformforge/agents/planner.py +116 -0
  19. platformforge/agents/playbook.py +51 -0
  20. platformforge/agents/referee.py +132 -0
  21. platformforge/agents/reviewers.py +113 -0
  22. platformforge/agents/roster.py +893 -0
  23. platformforge/agents/runledger.py +85 -0
  24. platformforge/agents/runstore.py +74 -0
  25. platformforge/agents/specialists.py +73 -0
  26. platformforge/agents/taskspec.py +94 -0
  27. platformforge/agents/uniqueness.py +115 -0
  28. platformforge/agents/verifier.py +106 -0
  29. platformforge/aiplat/__init__.py +4 -0
  30. platformforge/aiplat/models.py +175 -0
  31. platformforge/analytics/__init__.py +6 -0
  32. platformforge/analytics/capacity.py +109 -0
  33. platformforge/analytics/debt.py +92 -0
  34. platformforge/analytics/dx.py +69 -0
  35. platformforge/analytics/finops_v4.py +171 -0
  36. platformforge/analytics/goldenpath.py +131 -0
  37. platformforge/analytics/history.py +233 -0
  38. platformforge/analytics/measurement.py +149 -0
  39. platformforge/analytics/models.py +127 -0
  40. platformforge/analytics/opsanalytics.py +119 -0
  41. platformforge/analytics/policyintel.py +139 -0
  42. platformforge/analytics/reliability.py +106 -0
  43. platformforge/analytics/soak.py +135 -0
  44. platformforge/analytics/store.py +254 -0
  45. platformforge/bench.py +174 -0
  46. platformforge/cases/__init__.py +13 -0
  47. platformforge/cases/casefile.py +106 -0
  48. platformforge/cases/context_audit.py +134 -0
  49. platformforge/cases/ledgers.py +174 -0
  50. platformforge/cases/replay.py +142 -0
  51. platformforge/cases/routing_audit.py +162 -0
  52. platformforge/caveman/__init__.py +11 -0
  53. platformforge/caveman/compress.py +156 -0
  54. platformforge/cicd/__init__.py +16 -0
  55. platformforge/cicd/backstage.py +51 -0
  56. platformforge/cicd/dockerfile.py +64 -0
  57. platformforge/cicd/github_actions.py +122 -0
  58. platformforge/cicd/gitlab_ci.py +56 -0
  59. platformforge/cicd/gitops.py +130 -0
  60. platformforge/cli/__init__.py +0 -0
  61. platformforge/cli/fleetcmd.py +457 -0
  62. platformforge/cli/main.py +3832 -0
  63. platformforge/cloud/__init__.py +8 -0
  64. platformforge/cloud/aws.py +254 -0
  65. platformforge/cloud/common.py +69 -0
  66. platformforge/cloud/multicloud.py +143 -0
  67. platformforge/collect/__init__.py +11 -0
  68. platformforge/collect/detect.py +254 -0
  69. platformforge/context/__init__.py +10 -0
  70. platformforge/context/capsule.py +60 -0
  71. platformforge/context/gateway.py +227 -0
  72. platformforge/context/refs.py +49 -0
  73. platformforge/context/sufficiency.py +50 -0
  74. platformforge/core/__init__.py +1 -0
  75. platformforge/core/hashing.py +39 -0
  76. platformforge/core/ownership.py +220 -0
  77. platformforge/core/receipts.py +74 -0
  78. platformforge/core/redaction.py +97 -0
  79. platformforge/core/store.py +119 -0
  80. platformforge/core/workspace.py +89 -0
  81. platformforge/data/contracts/agent-handoff.schema.json +26 -0
  82. platformforge/data/contracts/agent-run.schema.json +27 -0
  83. platformforge/data/contracts/agent-spec.schema.json +43 -0
  84. platformforge/data/contracts/capability.schema.json +23 -0
  85. platformforge/data/contracts/change-event.schema.json +22 -0
  86. platformforge/data/contracts/change-intent.schema.json +62 -0
  87. platformforge/data/contracts/change-plan.schema.json +43 -0
  88. platformforge/data/contracts/collector-receipt.schema.json +36 -0
  89. platformforge/data/contracts/context-pack.schema.json +29 -0
  90. platformforge/data/contracts/debate.schema.json +31 -0
  91. platformforge/data/contracts/distribution-manifest-v1.schema.json +81 -0
  92. platformforge/data/contracts/drift-event.schema.json +21 -0
  93. platformforge/data/contracts/fact.schema.json +35 -0
  94. platformforge/data/contracts/finding.schema.json +20 -0
  95. platformforge/data/contracts/graph-v2.schema.json +67 -0
  96. platformforge/data/contracts/graph.schema.json +40 -0
  97. platformforge/data/contracts/install-receipt-v1.schema.json +49 -0
  98. platformforge/data/contracts/observation.schema.json +93 -0
  99. platformforge/data/contracts/receipt.schema.json +21 -0
  100. platformforge/data/contracts/recommendation.schema.json +32 -0
  101. platformforge/data/contracts/refusal.schema.json +16 -0
  102. platformforge/data/contracts/slo-contract.schema.json +25 -0
  103. platformforge/data/contracts/task-spec.schema.json +37 -0
  104. platformforge/data/contracts/workspace-manifest-v1.schema.json +62 -0
  105. platformforge/data/evals/cases/agent-budget-exhaustion/case.yaml +6 -0
  106. platformforge/data/evals/cases/agent-conflicting-specialists/case.yaml +11 -0
  107. platformforge/data/evals/cases/agent-fleet-optimization/case.yaml +13 -0
  108. platformforge/data/evals/cases/agent-host-no-subagents/case.yaml +6 -0
  109. platformforge/data/evals/cases/agent-incident-multi/case.yaml +13 -0
  110. platformforge/data/evals/cases/agent-missing-evidence/case.yaml +6 -0
  111. platformforge/data/evals/cases/agent-multi-domain-audit/case.yaml +14 -0
  112. platformforge/data/evals/cases/agent-production-change/case.yaml +15 -0
  113. platformforge/data/evals/cases/agent-security-mandatory/case.yaml +12 -0
  114. platformforge/data/evals/cases/agent-simple-iac/case.yaml +14 -0
  115. platformforge/data/evals/cases/approval-integrity-seal/case.yaml +6 -0
  116. platformforge/data/evals/cases/capacity-hot/case.yaml +7 -0
  117. platformforge/data/evals/cases/capacity-hot/fixture/capacity.json +5 -0
  118. platformforge/data/evals/cases/catalog-noowner/case.yaml +7 -0
  119. platformforge/data/evals/cases/catalog-noowner/fixture/catalog-info.yaml +6 -0
  120. platformforge/data/evals/cases/contract-fact/case.yaml +5 -0
  121. platformforge/data/evals/cases/contract-fact/fixture/doc.json +2 -0
  122. platformforge/data/evals/cases/cosign-unverified/case.yaml +8 -0
  123. platformforge/data/evals/cases/cosign-unverified/fixture/sig.json +33 -0
  124. platformforge/data/evals/cases/expected-delta-refusal/case.yaml +6 -0
  125. platformforge/data/evals/cases/finops-noattr/case.yaml +9 -0
  126. platformforge/data/evals/cases/finops-noattr/fixture/costs.json +7 -0
  127. platformforge/data/evals/cases/fleet-inv-absent-allocation-unknown/case.yaml +7 -0
  128. platformforge/data/evals/cases/fleet-inv-centrality-not-criticality/case.yaml +7 -0
  129. platformforge/data/evals/cases/fleet-inv-correlation-not-causality/case.yaml +7 -0
  130. platformforge/data/evals/cases/fleet-inv-dx-no-person-metrics/case.yaml +7 -0
  131. platformforge/data/evals/cases/fleet-inv-federation-restricted-denied/case.yaml +7 -0
  132. platformforge/data/evals/cases/fleet-inv-federation-secret-denied/case.yaml +7 -0
  133. platformforge/data/evals/cases/fleet-inv-history-window-deterministic/case.yaml +7 -0
  134. platformforge/data/evals/cases/fleet-inv-low-util-not-deletable/case.yaml +7 -0
  135. platformforge/data/evals/cases/fleet-inv-member-key-deterministic/case.yaml +7 -0
  136. platformforge/data/evals/cases/fleet-inv-missing-denominator/case.yaml +7 -0
  137. platformforge/data/evals/cases/fleet-inv-partial-coverage-visible/case.yaml +7 -0
  138. platformforge/data/evals/cases/fleet-inv-plan-changeintent-only/case.yaml +7 -0
  139. platformforge/data/evals/cases/fleet-inv-report-all-cited/case.yaml +7 -0
  140. platformforge/data/evals/cases/fleet-inv-small-sample-confidence/case.yaml +7 -0
  141. platformforge/data/evals/cases/fleet-inv-uncertain-optimization-suppressed/case.yaml +7 -0
  142. platformforge/data/evals/cases/gha-dangerous/case.yaml +12 -0
  143. platformforge/data/evals/cases/gha-dangerous/fixture/.github/workflows/ci.yaml +11 -0
  144. platformforge/data/evals/cases/gitops-v2/case.yaml +11 -0
  145. platformforge/data/evals/cases/gitops-v2/fixture/argo.yaml +41 -0
  146. platformforge/data/evals/cases/graph-k8s-blast/case.yaml +7 -0
  147. platformforge/data/evals/cases/graph-k8s-blast/fixture/deploy.yaml +15 -0
  148. platformforge/data/evals/cases/iac-module-nopin/case.yaml +8 -0
  149. platformforge/data/evals/cases/iac-module-nopin/fixture/main.tf +3 -0
  150. platformforge/data/evals/cases/iac-plan-replace/case.yaml +8 -0
  151. platformforge/data/evals/cases/iac-plan-replace/fixture/plan.json +12 -0
  152. platformforge/data/evals/cases/iac-sg-open/case.yaml +8 -0
  153. platformforge/data/evals/cases/iac-sg-open/fixture/main.tf +9 -0
  154. platformforge/data/evals/cases/iam-oidc/case.yaml +8 -0
  155. platformforge/data/evals/cases/iam-oidc/fixture/policy.json +11 -0
  156. platformforge/data/evals/cases/iam-wildcard-positive/case.yaml +8 -0
  157. platformforge/data/evals/cases/iam-wildcard-positive/fixture/policy.json +3 -0
  158. platformforge/data/evals/cases/k8s-bad-actor/case.yaml +19 -0
  159. platformforge/data/evals/cases/k8s-bad-actor/fixture/app.yaml +97 -0
  160. platformforge/data/evals/cases/k8s-beta-api/case.yaml +8 -0
  161. platformforge/data/evals/cases/k8s-beta-api/fixture/cr.yaml +7 -0
  162. platformforge/data/evals/cases/k8s-dep-api-boundary/case.yaml +15 -0
  163. platformforge/data/evals/cases/k8s-dep-api-boundary/fixture/app.yaml +25 -0
  164. platformforge/data/evals/cases/k8s-dep-api-clean/case.yaml +12 -0
  165. platformforge/data/evals/cases/k8s-dep-api-clean/fixture/app.yaml +29 -0
  166. platformforge/data/evals/cases/k8s-dep-api-unknown/case.yaml +11 -0
  167. platformforge/data/evals/cases/k8s-dep-api-unknown/fixture/app.yaml +25 -0
  168. platformforge/data/evals/cases/k8s-dep-api-v120/case.yaml +12 -0
  169. platformforge/data/evals/cases/k8s-dep-api-v120/fixture/app.yaml +25 -0
  170. platformforge/data/evals/cases/k8s-dep-api-v129/case.yaml +9 -0
  171. platformforge/data/evals/cases/k8s-dep-api-v129/fixture/app.yaml +25 -0
  172. platformforge/data/evals/cases/k8s-hpa-noreq/case.yaml +8 -0
  173. platformforge/data/evals/cases/k8s-hpa-noreq/fixture/app.yaml +28 -0
  174. platformforge/data/evals/cases/k8s-limits-boundary/case.yaml +10 -0
  175. platformforge/data/evals/cases/k8s-limits-boundary/fixture/app.yaml +15 -0
  176. platformforge/data/evals/cases/k8s-limits-negative/case.yaml +8 -0
  177. platformforge/data/evals/cases/k8s-limits-negative/fixture/deploy.yaml +15 -0
  178. platformforge/data/evals/cases/k8s-limits-positive/case.yaml +8 -0
  179. platformforge/data/evals/cases/k8s-limits-positive/fixture/deploy.yaml +14 -0
  180. platformforge/data/evals/cases/k8s-nopdb/case.yaml +7 -0
  181. platformforge/data/evals/cases/k8s-nopdb/fixture/app.yaml +34 -0
  182. platformforge/data/evals/cases/knowledge-registry/case.yaml +10 -0
  183. platformforge/data/evals/cases/kyverno-dep/case.yaml +10 -0
  184. platformforge/data/evals/cases/kyverno-dep/fixture/pol.yaml +18 -0
  185. platformforge/data/evals/cases/kyverno-legacy/case.yaml +8 -0
  186. platformforge/data/evals/cases/kyverno-legacy/fixture/pol.yaml +7 -0
  187. platformforge/data/evals/cases/live-aws-collect/case.yaml +11 -0
  188. platformforge/data/evals/cases/live-aws-collect/fixture/aws/ec2_describe-vpcs.json +15 -0
  189. platformforge/data/evals/cases/live-aws-collect/fixture/aws/iam_list-users.denied.txt +1 -0
  190. platformforge/data/evals/cases/live-aws-collect/fixture/aws/sts_get-caller-identity.json +5 -0
  191. platformforge/data/evals/cases/live-drift/case.yaml +8 -0
  192. platformforge/data/evals/cases/live-drift/fixture/after.json +22 -0
  193. platformforge/data/evals/cases/live-drift/fixture/before.json +22 -0
  194. platformforge/data/evals/cases/live-incident/case.yaml +7 -0
  195. platformforge/data/evals/cases/live-incident/fixture/changes.json +8 -0
  196. platformforge/data/evals/cases/live-incident/fixture/incident.json +7 -0
  197. platformforge/data/evals/cases/live-k8s-collect/case.yaml +10 -0
  198. platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_configmaps.denied.txt +1 -0
  199. platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_deployments.apps.json +25 -0
  200. platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_pods.json +17 -0
  201. platformforge/data/evals/cases/live-k8s-collect/fixture/kubectl/get_secrets.json +13 -0
  202. platformforge/data/evals/cases/live-multicluster/case.yaml +7 -0
  203. platformforge/data/evals/cases/live-multicluster/fixture/clusters.json +6 -0
  204. platformforge/data/evals/cases/live-plan/case.yaml +7 -0
  205. platformforge/data/evals/cases/live-plan/fixture/drift.json +8 -0
  206. platformforge/data/evals/cases/live-reconcile/case.yaml +7 -0
  207. platformforge/data/evals/cases/live-reconcile/fixture/desired.json +7 -0
  208. platformforge/data/evals/cases/live-reconcile/fixture/observed.json +18 -0
  209. platformforge/data/evals/cases/live-secrets-boundary/case.yaml +8 -0
  210. platformforge/data/evals/cases/live-secrets-boundary/fixture/envelope.json +16 -0
  211. platformforge/data/evals/cases/live-temporal-graph/case.yaml +10 -0
  212. platformforge/data/evals/cases/live-temporal-graph/fixture/graph.json +22 -0
  213. platformforge/data/evals/cases/ops-approval/case.yaml +6 -0
  214. platformforge/data/evals/cases/ops-convergence/case.yaml +6 -0
  215. platformforge/data/evals/cases/ops-execution-safety/case.yaml +6 -0
  216. platformforge/data/evals/cases/ops-intent/case.yaml +6 -0
  217. platformforge/data/evals/cases/ops-planning/case.yaml +6 -0
  218. platformforge/data/evals/cases/ops-policy/case.yaml +6 -0
  219. platformforge/data/evals/cases/ops-rollback/case.yaml +6 -0
  220. platformforge/data/evals/cases/ops-verification/case.yaml +6 -0
  221. platformforge/data/evals/cases/rollback-argocd-revision/case.yaml +6 -0
  222. platformforge/data/evals/cases/rollback-blind-inverse/case.yaml +6 -0
  223. platformforge/data/evals/cases/rollback-git-commit/case.yaml +6 -0
  224. platformforge/data/evals/cases/rollback-impossible/case.yaml +6 -0
  225. platformforge/data/evals/cases/rollback-k8s-annotation-restore/case.yaml +6 -0
  226. platformforge/data/evals/cases/rollback-k8s-scale/case.yaml +6 -0
  227. platformforge/data/evals/cases/rollback-material-tamper/case.yaml +6 -0
  228. platformforge/data/evals/cases/rollback-terraform-replan/case.yaml +6 -0
  229. platformforge/data/evals/cases/rollback-unknown/case.yaml +6 -0
  230. platformforge/data/evals/cases/routing-specialist/case.yaml +9 -0
  231. platformforge/data/evals/cases/rtk-roundtrip/case.yaml +5 -0
  232. platformforge/data/evals/cases/rtk-roundtrip/fixture/verbose.txt +300 -0
  233. platformforge/data/evals/cases/sbom-vuln/case.yaml +9 -0
  234. platformforge/data/evals/cases/sbom-vuln/fixture/sbom.json +9 -0
  235. platformforge/data/evals/cases/secret-prose-mixed/fixture/app.env +1 -0
  236. platformforge/data/evals/cases/secret-prose-mixed/fixture/policy-notes.md +7 -0
  237. platformforge/data/evals/cases/secrets-noleak/case.yaml +5 -0
  238. platformforge/data/evals/cases/secrets-noleak/fixture/app.env +2 -0
  239. platformforge/data/evals/cases/slo-fastburn/case.yaml +8 -0
  240. platformforge/data/evals/cases/slo-fastburn/fixture/events.yaml +2 -0
  241. platformforge/data/evals/cases/slo-fastburn/fixture/slo.yaml +4 -0
  242. platformforge/data/evals/cases/slo-noinput/case.yaml +8 -0
  243. platformforge/data/evals/cases/slo-noinput/fixture/events.yaml +1 -0
  244. platformforge/data/evals/cases/slo-noinput/fixture/slo.yaml +4 -0
  245. platformforge/data/evals/cases/sot-conflict/case.yaml +6 -0
  246. platformforge/data/evals/cases/token-budget/case.yaml +6 -0
  247. platformforge/data/evals/cases/token-budget/fixture/big.py +50 -0
  248. platformforge/data/evals/cases/xr-delete/case.yaml +8 -0
  249. platformforge/data/evals/cases/xr-delete/fixture/xr.yaml +10 -0
  250. platformforge/data/evals/cases/xr-graph-chain/case.yaml +8 -0
  251. platformforge/data/evals/cases/xr-graph-chain/fixture/xrd.yaml +90 -0
  252. platformforge/data/evals/cases/xr-negative-crd/case.yaml +8 -0
  253. platformforge/data/evals/cases/xr-negative-crd/fixture/widget.yaml +19 -0
  254. platformforge/data/evals/cases/xr-upbound-mr/case.yaml +8 -0
  255. platformforge/data/evals/cases/xr-upbound-mr/fixture/bucket.yaml +12 -0
  256. platformforge/data/evals/qpt-bench/change-review/case.yaml +5 -0
  257. platformforge/data/evals/qpt-bench/change-review/fixture/plan.json +9 -0
  258. platformforge/data/evals/qpt-bench/gha-security/case.yaml +5 -0
  259. platformforge/data/evals/qpt-bench/gha-security/fixture/ci.yml +10 -0
  260. platformforge/data/evals/qpt-bench/iam-investigate/case.yaml +5 -0
  261. platformforge/data/evals/qpt-bench/iam-investigate/fixture/policy.json +6 -0
  262. platformforge/data/evals/qpt-bench/incident-analysis/case.yaml +5 -0
  263. platformforge/data/evals/qpt-bench/incident-analysis/fixture/query.json +5 -0
  264. platformforge/data/evals/qpt-bench/incident-analysis/fixture/rules.yaml +6 -0
  265. platformforge/data/evals/qpt-bench/k8s-review/case.yaml +6 -0
  266. platformforge/data/evals/qpt-bench/k8s-review/fixture/deploy.yaml +15 -0
  267. platformforge/data/evals/qpt-bench/tf-security/case.yaml +5 -0
  268. platformforge/data/evals/qpt-bench/tf-security/fixture/main.tf +17 -0
  269. platformforge/data/knowledge/aws/eks.yaml +20 -0
  270. platformforge/data/knowledge/aws/iam.yaml +28 -0
  271. platformforge/data/knowledge/aws/network-exposure.yaml +19 -0
  272. platformforge/data/knowledge/aws/organizations.yaml +19 -0
  273. platformforge/data/knowledge/crossplane/composition-providers.yaml +20 -0
  274. platformforge/data/knowledge/crossplane/managed-resources.yaml +26 -0
  275. platformforge/data/knowledge/crossplane/v1-v2.yaml +25 -0
  276. platformforge/data/knowledge/kubernetes/api-deprecations.yaml +33 -0
  277. platformforge/data/knowledge/kubernetes/autoscaling-rbac.yaml +27 -0
  278. platformforge/data/knowledge/kubernetes/gateway-api.yaml +20 -0
  279. platformforge/data/knowledge/kubernetes/pod-security.yaml +26 -0
  280. platformforge/data/knowledge/sources.yaml +516 -0
  281. platformforge/data/knowledge/terraform/lifecycle.yaml +18 -0
  282. platformforge/data/knowledge/terraform/plan-semantics.yaml +26 -0
  283. platformforge/data/lab/chaos-pod-kill/chaos.yaml +3 -0
  284. platformforge/data/lab/chaos-pod-kill/facts.json +13 -0
  285. platformforge/data/lab/fleets/acme/capacity.yaml +35 -0
  286. platformforge/data/lab/fleets/acme/costs.yaml +25 -0
  287. platformforge/data/lab/fleets/acme/events.yaml +41 -0
  288. platformforge/data/lab/fleets/acme/fleet.yaml +51 -0
  289. platformforge/data/lab/fleets/acme/graph.yaml +45 -0
  290. platformforge/data/lab/fleets/acme/policies.yaml +28 -0
  291. platformforge/data/lab/fleets/acme/requests.yaml +24 -0
  292. platformforge/data/lab/scenarios/argo-bad-rollout/expected.yaml +6 -0
  293. platformforge/data/lab/scenarios/argo-bad-rollout/fixture/app.yaml +9 -0
  294. platformforge/data/lab/scenarios/aws-dump/expected.yaml +19 -0
  295. platformforge/data/lab/scenarios/aws-dump/fixture/compute.json +4 -0
  296. platformforge/data/lab/scenarios/aws-dump/fixture/iam.json +7 -0
  297. platformforge/data/lab/scenarios/aws-dump/fixture/network.json +6 -0
  298. platformforge/data/lab/scenarios/aws-dump/fixture/org.json +4 -0
  299. platformforge/data/lab/scenarios/aws-dump/fixture/storage.json +4 -0
  300. platformforge/data/lab/scenarios/container-unsigned/expected.yaml +5 -0
  301. platformforge/data/lab/scenarios/container-unsigned/fixture/supply.json +11 -0
  302. platformforge/data/lab/scenarios/cost-idle-workload/expected.yaml +5 -0
  303. platformforge/data/lab/scenarios/cost-idle-workload/fixture/costs.json +12 -0
  304. platformforge/data/lab/scenarios/crossplane-platform/expected.yaml +16 -0
  305. platformforge/data/lab/scenarios/crossplane-platform/fixture/negative.yaml +18 -0
  306. platformforge/data/lab/scenarios/crossplane-platform/fixture/xrd.yaml +90 -0
  307. platformforge/data/lab/scenarios/demo-platform/expected.yaml +16 -0
  308. platformforge/data/lab/scenarios/demo-platform/fixture/.github/workflows/ci.yml +10 -0
  309. platformforge/data/lab/scenarios/demo-platform/fixture/deploy.yaml +16 -0
  310. platformforge/data/lab/scenarios/demo-platform/fixture/infra.tf +5 -0
  311. platformforge/data/lab/scenarios/fleet-ai/expected.yaml +12 -0
  312. platformforge/data/lab/scenarios/fleet-capacity/expected.yaml +12 -0
  313. platformforge/data/lab/scenarios/fleet-cost/expected.yaml +18 -0
  314. platformforge/data/lab/scenarios/fleet-federation/expected.yaml +11 -0
  315. platformforge/data/lab/scenarios/fleet-golden-path/expected.yaml +18 -0
  316. platformforge/data/lab/scenarios/fleet-op-hotspot/expected.yaml +11 -0
  317. platformforge/data/lab/scenarios/fleet-ownership/expected.yaml +17 -0
  318. platformforge/data/lab/scenarios/fleet-partial/expected.yaml +10 -0
  319. platformforge/data/lab/scenarios/fleet-policy-friction/expected.yaml +10 -0
  320. platformforge/data/lab/scenarios/fleet-recurring-incident/expected.yaml +12 -0
  321. platformforge/data/lab/scenarios/fleet-report/expected.yaml +13 -0
  322. platformforge/data/lab/scenarios/fleet-risk/expected.yaml +18 -0
  323. platformforge/data/lab/scenarios/gitops-drift/expected.yaml +6 -0
  324. platformforge/data/lab/scenarios/gitops-drift/fixture/app.yaml +12 -0
  325. platformforge/data/lab/scenarios/k8s-bad-readiness/expected.yaml +6 -0
  326. platformforge/data/lab/scenarios/k8s-bad-readiness/fixture/deploy.yaml +23 -0
  327. platformforge/data/lab/scenarios/k8s-bad-readiness/fixture/netpol.yaml +9 -0
  328. platformforge/data/lab/scenarios/k8s-missing-limits/expected.yaml +6 -0
  329. platformforge/data/lab/scenarios/k8s-missing-limits/fixture/deploy.yaml +26 -0
  330. platformforge/data/lab/scenarios/k8s-missing-limits/fixture/netpol.yaml +9 -0
  331. platformforge/data/lab/scenarios/k8s-single-replica/expected.yaml +5 -0
  332. platformforge/data/lab/scenarios/k8s-single-replica/fixture/deploy.yaml +26 -0
  333. platformforge/data/lab/scenarios/k8s-single-replica/fixture/netpol.yaml +4 -0
  334. platformforge/data/lab/scenarios/live-aws-collect/expected.yaml +4 -0
  335. platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/ec2_describe-vpcs.json +15 -0
  336. platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/iam_list-users.denied.txt +1 -0
  337. platformforge/data/lab/scenarios/live-aws-collect/fixture/aws/sts_get-caller-identity.json +5 -0
  338. platformforge/data/lab/scenarios/live-k8s-collect/expected.yaml +4 -0
  339. platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_configmaps.denied.txt +1 -0
  340. platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_deployments.apps.json +25 -0
  341. platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_pods.json +17 -0
  342. platformforge/data/lab/scenarios/live-k8s-collect/fixture/kubectl/get_secrets.json +13 -0
  343. platformforge/data/lab/scenarios/live-kind/expected.yaml +14 -0
  344. platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_configmaps.denied.txt +1 -0
  345. platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_deployments.apps.json +25 -0
  346. platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_pods.json +17 -0
  347. platformforge/data/lab/scenarios/live-kind/fixture/kubectl/get_secrets.json +13 -0
  348. platformforge/data/lab/scenarios/network-policy-missing/expected.yaml +6 -0
  349. platformforge/data/lab/scenarios/network-policy-missing/fixture/deploy.yaml +26 -0
  350. platformforge/data/lab/scenarios/ops-bad-rollout/expected.yaml +3 -0
  351. platformforge/data/lab/scenarios/ops-bad-rollout/fixture/ops.yaml +33 -0
  352. platformforge/data/lab/scenarios/ops-break-glass/expected.yaml +3 -0
  353. platformforge/data/lab/scenarios/ops-break-glass/fixture/ops.yaml +21 -0
  354. platformforge/data/lab/scenarios/ops-broken-selector/expected.yaml +3 -0
  355. platformforge/data/lab/scenarios/ops-broken-selector/fixture/ops.yaml +21 -0
  356. platformforge/data/lab/scenarios/ops-changed-plan-hash/expected.yaml +3 -0
  357. platformforge/data/lab/scenarios/ops-changed-plan-hash/fixture/ops.yaml +20 -0
  358. platformforge/data/lab/scenarios/ops-conflicting-operation/expected.yaml +3 -0
  359. platformforge/data/lab/scenarios/ops-conflicting-operation/fixture/ops.yaml +21 -0
  360. platformforge/data/lab/scenarios/ops-expired-approval/expected.yaml +3 -0
  361. platformforge/data/lab/scenarios/ops-expired-approval/fixture/ops.yaml +20 -0
  362. platformforge/data/lab/scenarios/ops-partial-failure/expected.yaml +3 -0
  363. platformforge/data/lab/scenarios/ops-partial-failure/fixture/ops.yaml +24 -0
  364. platformforge/data/lab/scenarios/ops-policy-denial/expected.yaml +3 -0
  365. platformforge/data/lab/scenarios/ops-policy-denial/fixture/ops.yaml +17 -0
  366. platformforge/data/lab/scenarios/ops-replica-drift/expected.yaml +3 -0
  367. platformforge/data/lab/scenarios/ops-replica-drift/fixture/ops.yaml +28 -0
  368. platformforge/data/lab/scenarios/ops-rollback-annotate/expected.yaml +3 -0
  369. platformforge/data/lab/scenarios/ops-rollback-annotate/fixture/ops.yaml +41 -0
  370. platformforge/data/lab/scenarios/ops-rollback-gitops/expected.yaml +3 -0
  371. platformforge/data/lab/scenarios/ops-rollback-gitops/fixture/ops.yaml +38 -0
  372. platformforge/data/lab/scenarios/ops-rollback-impossible/expected.yaml +3 -0
  373. platformforge/data/lab/scenarios/ops-rollback-impossible/fixture/ops.yaml +34 -0
  374. platformforge/data/lab/scenarios/ops-rollback-scale/expected.yaml +3 -0
  375. platformforge/data/lab/scenarios/ops-rollback-scale/fixture/ops.yaml +40 -0
  376. platformforge/data/lab/scenarios/ops-rollback-terraform/expected.yaml +3 -0
  377. platformforge/data/lab/scenarios/ops-rollback-terraform/fixture/ops.yaml +38 -0
  378. platformforge/data/lab/scenarios/ops-rollback-terraform/fixture/tf/tfplan.bin +1 -0
  379. platformforge/data/lab/scenarios/ops-stale-observation/expected.yaml +3 -0
  380. platformforge/data/lab/scenarios/ops-stale-observation/fixture/ops.yaml +20 -0
  381. platformforge/data/lab/scenarios/secret-exposure/expected.yaml +5 -0
  382. platformforge/data/lab/scenarios/secret-exposure/fixture/app.env +2 -0
  383. platformforge/data/lab/scenarios/slo-burn/expected.yaml +5 -0
  384. platformforge/data/lab/scenarios/slo-burn/fixture/events.yaml +2 -0
  385. platformforge/data/lab/scenarios/slo-burn/fixture/slo.yaml +4 -0
  386. platformforge/data/lab/scenarios/terraform-iam-wildcard/expected.yaml +5 -0
  387. platformforge/data/lab/scenarios/terraform-iam-wildcard/fixture/policy.json +14 -0
  388. platformforge/data/lab/scenarios/terraform-public-bucket/expected.yaml +6 -0
  389. platformforge/data/lab/scenarios/terraform-public-bucket/fixture/main.tf +5 -0
  390. platformforge/data/portable_skills/platformforge-agents/SKILL.md +47 -0
  391. platformforge/data/portable_skills/platformforge-change/SKILL.md +41 -0
  392. platformforge/data/portable_skills/platformforge-core/SKILL.md +62 -0
  393. platformforge/data/portable_skills/platformforge-economy/SKILL.md +42 -0
  394. platformforge/data/portable_skills/platformforge-finops/SKILL.md +34 -0
  395. platformforge/data/portable_skills/platformforge-fleet/SKILL.md +39 -0
  396. platformforge/data/portable_skills/platformforge-governance/SKILL.md +76 -0
  397. platformforge/data/portable_skills/platformforge-graph/SKILL.md +46 -0
  398. platformforge/data/portable_skills/platformforge-lab/SKILL.md +39 -0
  399. platformforge/data/portable_skills/platformforge-sdd/SKILL.md +39 -0
  400. platformforge/data/portable_skills/platformforge-security/SKILL.md +38 -0
  401. platformforge/data/portable_skills/platformforge-sre/SKILL.md +41 -0
  402. platformforge/data/rules/catalog/finops.yaml +26 -0
  403. platformforge/data/rules/catalog/gitops.yaml +161 -0
  404. platformforge/data/rules/catalog/iac.yaml +84 -0
  405. platformforge/data/rules/catalog/k8s.yaml +322 -0
  406. platformforge/data/rules/catalog/orchestration.yaml +99 -0
  407. platformforge/data/rules/catalog/product.yaml +42 -0
  408. platformforge/data/rules/catalog/routing.yaml +122 -0
  409. platformforge/data/rules/catalog/security.yaml +159 -0
  410. platformforge/data/rules/catalog/sre.yaml +67 -0
  411. platformforge/diagnose/__init__.py +75 -0
  412. platformforge/distribution/__init__.py +18 -0
  413. platformforge/distribution/bundle.py +127 -0
  414. platformforge/distribution/hostflow.py +247 -0
  415. platformforge/distribution/models.py +80 -0
  416. platformforge/distribution/service.py +225 -0
  417. platformforge/economy/__init__.py +27 -0
  418. platformforge/economy/bench.py +66 -0
  419. platformforge/economy/budget.py +198 -0
  420. platformforge/economy/cache.py +287 -0
  421. platformforge/economy/checkpoint.py +112 -0
  422. platformforge/economy/collection.py +142 -0
  423. platformforge/economy/doctor.py +207 -0
  424. platformforge/economy/engine.py +158 -0
  425. platformforge/economy/envelope.py +61 -0
  426. platformforge/economy/fleetpack.py +86 -0
  427. platformforge/economy/ledger.py +232 -0
  428. platformforge/economy/plan.py +131 -0
  429. platformforge/economy/pricing.py +101 -0
  430. platformforge/economy/qpt.py +286 -0
  431. platformforge/economy/reconcile.py +86 -0
  432. platformforge/economy/verifyplan.py +107 -0
  433. platformforge/economy/waste.py +123 -0
  434. platformforge/evals/__init__.py +16 -0
  435. platformforge/evals/coverage.py +140 -0
  436. platformforge/evals/fleet_invariants.py +260 -0
  437. platformforge/evals/ops_invariants.py +158 -0
  438. platformforge/evals/runner.py +541 -0
  439. platformforge/federation/__init__.py +4 -0
  440. platformforge/federation/node.py +135 -0
  441. platformforge/finops/__init__.py +14 -0
  442. platformforge/finops/alloc.py +31 -0
  443. platformforge/finops/costs.py +79 -0
  444. platformforge/finops/focus.py +100 -0
  445. platformforge/finops/graph_cost.py +51 -0
  446. platformforge/finops/ingest.py +144 -0
  447. platformforge/finops/insights.py +193 -0
  448. platformforge/finops/unit.py +36 -0
  449. platformforge/fleet/__init__.py +5 -0
  450. platformforge/fleet/loader.py +73 -0
  451. platformforge/fleet/models.py +155 -0
  452. platformforge/fleet/orggraph.py +172 -0
  453. platformforge/fleet/query.py +154 -0
  454. platformforge/forge/__init__.py +9 -0
  455. platformforge/forge/collect.py +88 -0
  456. platformforge/forge/delegate.py +77 -0
  457. platformforge/forge/manifest.py +106 -0
  458. platformforge/freeze/__init__.py +22 -0
  459. platformforge/freeze/exceptions.py +56 -0
  460. platformforge/freeze/manifest.py +167 -0
  461. platformforge/freeze/snapshots.py +108 -0
  462. platformforge/graph/__init__.py +39 -0
  463. platformforge/graph/backend.py +174 -0
  464. platformforge/graph/bench.py +245 -0
  465. platformforge/graph/build.py +130 -0
  466. platformforge/graph/delivery.py +183 -0
  467. platformforge/graph/diff.py +292 -0
  468. platformforge/graph/events.py +116 -0
  469. platformforge/graph/migrate.py +44 -0
  470. platformforge/graph/model.py +264 -0
  471. platformforge/graph/persist.py +97 -0
  472. platformforge/graph/query.py +290 -0
  473. platformforge/graph/temporal.py +78 -0
  474. platformforge/graph/vocab.py +63 -0
  475. platformforge/graphview.py +94 -0
  476. platformforge/iac/__init__.py +7 -0
  477. platformforge/iac/drift.py +53 -0
  478. platformforge/iac/plan.py +84 -0
  479. platformforge/iac/terraform.py +123 -0
  480. platformforge/install/__init__.py +1 -0
  481. platformforge/install/service.py +421 -0
  482. platformforge/k8s/__init__.py +8 -0
  483. platformforge/k8s/helm.py +152 -0
  484. platformforge/k8s/hubble.py +87 -0
  485. platformforge/k8s/manifests.py +513 -0
  486. platformforge/knowledge/__init__.py +1 -0
  487. platformforge/knowledge/packs.py +157 -0
  488. platformforge/knowledge/registry.py +249 -0
  489. platformforge/knowledge/select.py +127 -0
  490. platformforge/lab/__init__.py +5 -0
  491. platformforge/lab/chaos.py +71 -0
  492. platformforge/lab/fleetlab.py +351 -0
  493. platformforge/lab/opslab.py +271 -0
  494. platformforge/lab/runner.py +219 -0
  495. platformforge/live/__init__.py +36 -0
  496. platformforge/live/budget.py +92 -0
  497. platformforge/live/capability.py +80 -0
  498. platformforge/live/collectors/__init__.py +6 -0
  499. platformforge/live/collectors/aws.py +370 -0
  500. platformforge/live/collectors/aws_transport.py +98 -0
  501. platformforge/live/collectors/k8s.py +389 -0
  502. platformforge/live/collectors/k8s_transport.py +85 -0
  503. platformforge/live/cursors.py +48 -0
  504. platformforge/live/drift.py +130 -0
  505. platformforge/live/envelope.py +85 -0
  506. platformforge/live/federation.py +113 -0
  507. platformforge/live/fixtures.py +168 -0
  508. platformforge/live/identity.py +258 -0
  509. platformforge/live/incident.py +200 -0
  510. platformforge/live/models.py +435 -0
  511. platformforge/live/reconcile.py +382 -0
  512. platformforge/live/remediate.py +169 -0
  513. platformforge/live/store.py +256 -0
  514. platformforge/live/topology.py +249 -0
  515. platformforge/mcp/__init__.py +7 -0
  516. platformforge/mcp/parity.py +69 -0
  517. platformforge/mcp/registry.py +427 -0
  518. platformforge/mcp/server.py +84 -0
  519. platformforge/mcp/tools.py +732 -0
  520. platformforge/models/__init__.py +21 -0
  521. platformforge/models/base.py +214 -0
  522. platformforge/observe/__init__.py +26 -0
  523. platformforge/observe/capacity.py +39 -0
  524. platformforge/observe/dr.py +67 -0
  525. platformforge/observe/grafana.py +76 -0
  526. platformforge/observe/incident.py +58 -0
  527. platformforge/observe/otel.py +135 -0
  528. platformforge/observe/postmortem.py +72 -0
  529. platformforge/observe/prometheus.py +88 -0
  530. platformforge/observe/slo.py +125 -0
  531. platformforge/ops/__init__.py +8 -0
  532. platformforge/ops/actions.py +215 -0
  533. platformforge/ops/analytics.py +83 -0
  534. platformforge/ops/approval.py +275 -0
  535. platformforge/ops/autorem.py +101 -0
  536. platformforge/ops/config.py +175 -0
  537. platformforge/ops/engine.py +758 -0
  538. platformforge/ops/envelope.py +135 -0
  539. platformforge/ops/executors/__init__.py +1 -0
  540. platformforge/ops/executors/base.py +211 -0
  541. platformforge/ops/executors/git.py +77 -0
  542. platformforge/ops/executors/gitops.py +134 -0
  543. platformforge/ops/executors/terraform.py +84 -0
  544. platformforge/ops/gates.py +135 -0
  545. platformforge/ops/goldenpath.py +180 -0
  546. platformforge/ops/material.py +181 -0
  547. platformforge/ops/models.py +421 -0
  548. platformforge/ops/operation.py +335 -0
  549. platformforge/ops/opgraph.py +233 -0
  550. platformforge/ops/policy.py +275 -0
  551. platformforge/ops/preconditions.py +109 -0
  552. platformforge/ops/registry.py +101 -0
  553. platformforge/ops/risk.py +216 -0
  554. platformforge/ops/rollback.py +287 -0
  555. platformforge/ops/rollback_builders.py +163 -0
  556. platformforge/ops/runbook.py +269 -0
  557. platformforge/ops/simulate.py +125 -0
  558. platformforge/ops/source_of_truth.py +198 -0
  559. platformforge/ops/store.py +157 -0
  560. platformforge/ops/verify.py +190 -0
  561. platformforge/optimize/__init__.py +4 -0
  562. platformforge/optimize/engine.py +109 -0
  563. platformforge/optimize/models.py +68 -0
  564. platformforge/optimize/scan.py +196 -0
  565. platformforge/plan/__init__.py +107 -0
  566. platformforge/portable_cli.py +199 -0
  567. platformforge/portal_adapter.py +740 -0
  568. platformforge/portal_assets/chunk-7_l-b4Tr.js +1 -0
  569. platformforge/portal_assets/chunk-B0Iad6pD.js +1 -0
  570. platformforge/portal_assets/chunk-B605XnCF.js +1 -0
  571. platformforge/portal_assets/chunk-B7N3TXcn.js +1 -0
  572. platformforge/portal_assets/chunk-BAj6yGem.js +1 -0
  573. platformforge/portal_assets/chunk-BGUitn2r.js +1 -0
  574. platformforge/portal_assets/chunk-BVKnCv85.js +1 -0
  575. platformforge/portal_assets/chunk-BYyM9V_w.js +1 -0
  576. platformforge/portal_assets/chunk-BaQMhq-G.js +1 -0
  577. platformforge/portal_assets/chunk-BavOrxVm.js +1 -0
  578. platformforge/portal_assets/chunk-Bb0C24YV.js +1 -0
  579. platformforge/portal_assets/chunk-BfCFGPzs.js +1 -0
  580. platformforge/portal_assets/chunk-C2t4OUVu.js +1 -0
  581. platformforge/portal_assets/chunk-C3pI4QjU.js +1 -0
  582. platformforge/portal_assets/chunk-C4gpbtbP.js +1 -0
  583. platformforge/portal_assets/chunk-CGWeHhKY.js +1 -0
  584. platformforge/portal_assets/chunk-CJCT86Dr.js +1 -0
  585. platformforge/portal_assets/chunk-CXcsLDPJ.js +1 -0
  586. platformforge/portal_assets/chunk-CcjxoLxw.js +1 -0
  587. platformforge/portal_assets/chunk-D-gBPXHa.js +1 -0
  588. platformforge/portal_assets/chunk-D3eZogIe.js +1 -0
  589. platformforge/portal_assets/chunk-DQU0VyoQ.js +1 -0
  590. platformforge/portal_assets/chunk-DqWNkF1O.js +1 -0
  591. platformforge/portal_assets/chunk-DuPPkSIE.js +3 -0
  592. platformforge/portal_assets/chunk-DyIHOW_a.js +1 -0
  593. platformforge/portal_assets/chunk-Fx4zSGL5.js +1 -0
  594. platformforge/portal_assets/chunk-UBKt05Rg.js +1 -0
  595. platformforge/portal_assets/chunk-htnJDQEY.js +1 -0
  596. platformforge/portal_assets/chunk-kXRFMGqe.js +1 -0
  597. platformforge/portal_assets/chunk-qe3u763a.js +1 -0
  598. platformforge/portal_assets/favicon.ico +0 -0
  599. platformforge/portal_assets/index.html +13 -0
  600. platformforge/portal_assets/main-QDFVZK3T.js +5 -0
  601. platformforge/portal_assets/styles-MQEBDFDX.css +1 -0
  602. platformforge/product/__init__.py +10 -0
  603. platformforge/product/backstage.py +49 -0
  604. platformforge/product/catalog.py +74 -0
  605. platformforge/product/crossplane.py +341 -0
  606. platformforge/product/golden_paths/__init__.py +5 -0
  607. platformforge/product/golden_paths/engine.py +97 -0
  608. platformforge/product/golden_paths/library.yaml +198 -0
  609. platformforge/product/golden_paths/model.py +84 -0
  610. platformforge/product/maturity.py +107 -0
  611. platformforge/product/scorecards.py +84 -0
  612. platformforge/resources.py +36 -0
  613. platformforge/risk/__init__.py +5 -0
  614. platformforge/risk/engine.py +95 -0
  615. platformforge/risk/signals.py +49 -0
  616. platformforge/routing/__init__.py +13 -0
  617. platformforge/routing/decision.py +191 -0
  618. platformforge/routing/router.py +328 -0
  619. platformforge/rtk/__init__.py +9 -0
  620. platformforge/rtk/compact.py +127 -0
  621. platformforge/rtk/parsers.py +199 -0
  622. platformforge/rules/__init__.py +5 -0
  623. platformforge/rules/engine.py +345 -0
  624. platformforge/sandbox/__init__.py +7 -0
  625. platformforge/sandbox/env.py +151 -0
  626. platformforge/sandbox/review.py +92 -0
  627. platformforge/sdd/__init__.py +8 -0
  628. platformforge/sdd/lifecycle.py +239 -0
  629. platformforge/security/__init__.py +19 -0
  630. platformforge/security/cosign.py +67 -0
  631. platformforge/security/iam.py +112 -0
  632. platformforge/security/identity.py +84 -0
  633. platformforge/security/kyverno.py +106 -0
  634. platformforge/security/sbom.py +59 -0
  635. platformforge/security/scan.py +63 -0
  636. platformforge/security/slsa.py +86 -0
  637. platformforge/security/supply.py +62 -0
  638. platformforge/tokensave/__init__.py +20 -0
  639. platformforge/tokensave/budget.py +62 -0
  640. platformforge/tokensave/estimate.py +21 -0
  641. platformforge/tokensave/index.py +172 -0
  642. platformforge/tokensave/ledger.py +121 -0
  643. platformforge/tokensave/packs.py +285 -0
  644. platformforge/ui/__init__.py +28 -0
  645. platformforge/ui/app.py +465 -0
  646. platformforge/ui/home.py +113 -0
  647. platformforge/ui/i18n.py +143 -0
  648. platformforge/ui/kit.py +689 -0
  649. platformforge/ui/screen.py +628 -0
  650. platformforge/ui/tui.py +94 -0
  651. platformforge/ui/wizard.py +309 -0
  652. platformforge/workspace/__init__.py +13 -0
  653. platformforge/workspace/models.py +35 -0
  654. platformforge/workspace/service.py +69 -0
  655. platformforge-0.1.0.dist-info/METADATA +212 -0
  656. platformforge-0.1.0.dist-info/RECORD +659 -0
  657. platformforge-0.1.0.dist-info/WHEEL +4 -0
  658. platformforge-0.1.0.dist-info/entry_points.txt +3 -0
  659. 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
+ }