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,198 @@
1
+ # §75 Golden Path seed library — seven starter paths covering the common
2
+ # platform shapes. Each declares the full §74 contract incl. its escape
3
+ # hatch (§77). Paths are declared knowledge (T5), not observed state.
4
+ schema: platformforge/golden-paths/v1
5
+ paths:
6
+ - id: rest-service
7
+ name: REST Service
8
+ version: "1.0.0"
9
+ use_case: "HTTP API deployed to the cluster with CI, scanning, SLO"
10
+ inputs:
11
+ - {name: service_name, required: true}
12
+ - {name: language, required: true, enum: [python, go, java, node]}
13
+ - {name: team, required: true}
14
+ outputs: [repository, pipeline, manifests, slo_contract, runbook]
15
+ steps:
16
+ - {id: scaffold, produces: repository}
17
+ - {id: ci, produces: pipeline}
18
+ - {id: secure, produces: sbom}
19
+ - {id: deploy, produces: manifests}
20
+ - {id: observe, produces: slo_contract}
21
+ policies: [pf-sec-baseline, pf-slo-required, pf-cost-tagging]
22
+ ownership: {owner_field: team, reviewer: platform-team}
23
+ observability: {metrics: required, traces: required, logs: required,
24
+ dashboards: generated}
25
+ security: {sbom: required, image_signing: required,
26
+ network_policy: default-deny}
27
+ cost: {tag_required: [team, service], budget_alert: true}
28
+ slo: {availability: 99.9, latency_p99_ms: 500}
29
+ escape_hatches:
30
+ happy_path: "scaffold → CI green → deploy to dev → promote"
31
+ supported_customizations: [replicas, resources, ingress_host,
32
+ autoscaling]
33
+ escape_hatch: "file a platform.ticket with category path-escape;
34
+ platform team reviews within SLA"
35
+ unsupported_state: "custom controllers, host mounts, privileged
36
+ containers without exception record"
37
+ supported_variants: [public-api, internal-api]
38
+
39
+ - id: worker
40
+ name: Background Worker
41
+ version: "1.0.0"
42
+ use_case: "queue/topic consumer with retries, DLQ, autoscaling"
43
+ inputs:
44
+ - {name: worker_name, required: true}
45
+ - {name: source, required: true, enum: [sqs, kafka, pubsub]}
46
+ - {name: team, required: true}
47
+ outputs: [repository, pipeline, manifests, dlq, slo_contract]
48
+ steps:
49
+ - {id: scaffold, produces: repository}
50
+ - {id: ci, produces: pipeline}
51
+ - {id: secure, produces: sbom}
52
+ - {id: deploy, produces: manifests}
53
+ - {id: observe, produces: slo_contract}
54
+ policies: [pf-sec-baseline, pf-dlq-required, pf-cost-tagging]
55
+ ownership: {owner_field: team, reviewer: platform-team}
56
+ observability: {metrics: required, traces: recommended,
57
+ logs: required}
58
+ security: {sbom: required, image_signing: required}
59
+ cost: {tag_required: [team, service]}
60
+ slo: {processing_lag_seconds: 60, success_rate: 99.5}
61
+ escape_hatches:
62
+ happy_path: "scaffold → wire trigger → deploy"
63
+ supported_customizations: [concurrency, batch_size, dlq_policy]
64
+ escape_hatch: "platform.ticket category path-escape"
65
+ unsupported_state: "custom brokers without platform review"
66
+ supported_variants: [fifo, at-least-once]
67
+
68
+ - id: scheduled-job
69
+ name: Scheduled Job
70
+ version: "1.0.0"
71
+ use_case: "cron workload with timeout, retries, alert on failure"
72
+ inputs:
73
+ - {name: job_name, required: true}
74
+ - {name: schedule, required: true}
75
+ - {name: team, required: true}
76
+ outputs: [repository, pipeline, cronjob_manifest, alert_rules]
77
+ steps:
78
+ - {id: scaffold, produces: repository}
79
+ - {id: ci, produces: pipeline}
80
+ - {id: deploy, produces: cronjob_manifest}
81
+ policies: [pf-sec-baseline, pf-cost-tagging]
82
+ ownership: {owner_field: team, reviewer: platform-team}
83
+ observability: {metrics: required, logs: required,
84
+ alert_on_failure: true}
85
+ security: {sbom: required}
86
+ cost: {tag_required: [team, job]}
87
+ slo: {completion_rate: 99.0}
88
+ escape_hatches:
89
+ happy_path: "scaffold → schedule → alert wired"
90
+ supported_customizations: [schedule, timeout, retries]
91
+ escape_hatch: "platform.ticket category path-escape"
92
+ unsupported_state: "jobs needing state beyond idempotent retries"
93
+ supported_variants: [daily, hourly]
94
+
95
+ - id: kubernetes-service
96
+ name: Kubernetes Service
97
+ version: "1.0.0"
98
+ use_case: "containerized workload with HPA, PDB, netpol, GitOps"
99
+ inputs:
100
+ - {name: service_name, required: true}
101
+ - {name: image, required: true}
102
+ - {name: team, required: true}
103
+ outputs: [manifests, gitops_application, slo_contract]
104
+ steps:
105
+ - {id: generate, produces: manifests}
106
+ - {id: gitops, produces: gitops_application}
107
+ - {id: observe, produces: slo_contract}
108
+ policies: [pf-k8s-baseline, pf-netpol-default-deny, pf-pdb-multi-replica]
109
+ ownership: {owner_field: team, reviewer: platform-team}
110
+ observability: {metrics: required, dashboards: generated}
111
+ security: {network_policy: default-deny, pod_security: restricted}
112
+ cost: {tag_required: [team, service]}
113
+ slo: {availability: 99.9}
114
+ escape_hatches:
115
+ happy_path: "generate manifests → argocd app → deploys"
116
+ supported_customizations: [hpa_bounds, pdb, env_vars]
117
+ escape_hatch: "platform.ticket category path-escape"
118
+ unsupported_state: "privileged pods, host networking"
119
+ supported_variants: [stateless, stateful-rwo]
120
+
121
+ - id: terraform-workload
122
+ name: Terraform Workload
123
+ version: "1.0.0"
124
+ use_case: "IaC-managed infra with plan-gate, drift watch, cost tags"
125
+ inputs:
126
+ - {name: module_name, required: true}
127
+ - {name: provider, required: true, enum: [aws, azurerm, google]}
128
+ - {name: team, required: true}
129
+ outputs: [module_repo, ci_plan_gate, drift_monitor]
130
+ steps:
131
+ - {id: scaffold, produces: module_repo}
132
+ - {id: ci, produces: ci_plan_gate}
133
+ - {id: drift, produces: drift_monitor}
134
+ policies: [pf-iac-review, pf-cost-tagging]
135
+ ownership: {owner_field: team, reviewer: platform-team}
136
+ observability: {plan_artifacts: retained, drift_alerts: true}
137
+ security: {policy_check: required, state_backend: remote}
138
+ cost: {tag_required: [team], estimate_in_pr: true}
139
+ slo: {}
140
+ escape_hatches:
141
+ happy_path: "module → plan PR → review → apply by CI"
142
+ supported_customizations: [variables, outputs]
143
+ escape_hatch: "manual apply only via break-glass with receipt"
144
+ unsupported_state: "local state, console changes outside IaC"
145
+ supported_variants: [module, stack]
146
+
147
+ - id: database-request
148
+ name: Database Request
149
+ version: "1.0.0"
150
+ use_case: "managed datastore with backup, access policy, cost center"
151
+ inputs:
152
+ - {name: db_name, required: true}
153
+ - {name: engine, required: true,
154
+ enum: [postgres, mysql, dynamodb, redis]}
155
+ - {name: team, required: true}
156
+ - {name: data_classification, required: true}
157
+ outputs: [iac_module, access_policy, backup_policy]
158
+ steps:
159
+ - {id: request, produces: ticket}
160
+ - {id: provision, produces: iac_module}
161
+ - {id: secure, produces: access_policy}
162
+ policies: [pf-data-classification, pf-backup-required]
163
+ ownership: {owner_field: team, reviewer: dba-team}
164
+ observability: {metrics: required, slow_query_alerts: true}
165
+ security: {encryption: required, network: private-only}
166
+ cost: {tag_required: [team, db_name], budget_alert: true}
167
+ slo: {backup_rpo_hours: 24}
168
+ escape_hatches:
169
+ happy_path: "request → module PR → provisioned"
170
+ supported_customizations: [size, replicas, engine_version]
171
+ escape_hatch: "platform.ticket category database-exception"
172
+ unsupported_state: "public endpoints, unencrypted storage"
173
+ supported_variants: [dev, prod]
174
+
175
+ - id: queue-topic
176
+ name: Queue/Topic Request
177
+ version: "1.0.0"
178
+ use_case: "messaging resource with DLQ, retention, consumer wiring"
179
+ inputs:
180
+ - {name: resource_name, required: true}
181
+ - {name: kind, required: true, enum: [queue, topic]}
182
+ - {name: team, required: true}
183
+ outputs: [iac_module, dlq, consumer_contract]
184
+ steps:
185
+ - {id: request, produces: ticket}
186
+ - {id: provision, produces: iac_module}
187
+ policies: [pf-dlq-required, pf-cost-tagging]
188
+ ownership: {owner_field: team, reviewer: platform-team}
189
+ observability: {metrics: required, lag_alerts: true}
190
+ security: {encryption: required, access: scoped}
191
+ cost: {tag_required: [team]}
192
+ slo: {delivery_lag_seconds: 30}
193
+ escape_hatches:
194
+ happy_path: "request → module → wired"
195
+ supported_customizations: [retention, max_message_size]
196
+ escape_hatch: "platform.ticket category messaging-exception"
197
+ unsupported_state: "public topics, no DLQ"
198
+ supported_variants: [standard, fifo]
@@ -0,0 +1,84 @@
1
+ """§74 Golden Path contract — a path is a governed capability, not a
2
+ template. The model binds repo+CI+security+artifact+deployment+runtime+
3
+ observability+SLO+ownership+cost (§76) and every path documents its
4
+ escape hatch (§77): happy path, supported customizations, the escape
5
+ route, and what is explicitly unsupported.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Any
12
+
13
+ REQUIRED_FIELDS = ("id", "name", "version", "use_case", "inputs",
14
+ "outputs", "steps", "policies", "ownership",
15
+ "observability", "security", "cost", "slo",
16
+ "escape_hatches", "supported_variants")
17
+
18
+
19
+ @dataclass
20
+ class GoldenPath:
21
+ id: str
22
+ name: str
23
+ version: str
24
+ use_case: str
25
+ inputs: list[dict[str, Any]] = field(default_factory=list)
26
+ outputs: list[str] = field(default_factory=list)
27
+ steps: list[dict[str, Any]] = field(default_factory=list)
28
+ policies: list[str] = field(default_factory=list)
29
+ ownership: dict[str, Any] = field(default_factory=dict)
30
+ observability: dict[str, Any] = field(default_factory=dict)
31
+ security: dict[str, Any] = field(default_factory=dict)
32
+ cost: dict[str, Any] = field(default_factory=dict)
33
+ slo: dict[str, Any] = field(default_factory=dict)
34
+ escape_hatches: dict[str, Any] = field(default_factory=dict)
35
+ supported_variants: list[str] = field(default_factory=list)
36
+
37
+ @classmethod
38
+ def from_dict(cls, doc: dict[str, Any]) -> GoldenPath:
39
+ pid = doc.get("id", "?")
40
+ missing = [f for f in REQUIRED_FIELDS if f not in doc]
41
+ if missing:
42
+ raise ValueError(f"golden path {pid} missing "
43
+ f"required fields: {missing}")
44
+ # §96 — structural validation, not just presence.
45
+ for i in doc["inputs"]:
46
+ if not isinstance(i, dict) or not i.get("name"):
47
+ raise ValueError(f"golden path {pid}: input without name")
48
+ for s in doc["steps"]:
49
+ if not isinstance(s, dict) or not s.get("id") \
50
+ or not s.get("produces"):
51
+ raise ValueError(f"golden path {pid}: step without "
52
+ "id/produces")
53
+ if not doc["ownership"].get("owner_field"):
54
+ raise ValueError(f"golden path {pid}: ownership lacks "
55
+ "owner_field")
56
+ for f in ("observability", "security", "cost"):
57
+ if not isinstance(doc[f], dict) or not doc[f]:
58
+ raise ValueError(f"golden path {pid}: {f} must be a "
59
+ "non-empty mapping")
60
+ # slo is required as a key but may be {} — infra paths produce no
61
+ # service-level SLO; empty is an honest "not applicable".
62
+ if not isinstance(doc["slo"], dict):
63
+ raise TypeError(f"golden path {pid}: slo must be a mapping")
64
+ for f in ("happy_path", "escape_hatch"):
65
+ if not doc["escape_hatches"].get(f):
66
+ raise ValueError(f"golden path {pid}: escape_hatches "
67
+ f"lacks {f}")
68
+ return cls(**{f: doc[f] for f in REQUIRED_FIELDS})
69
+
70
+ def capability(self) -> dict[str, Any]:
71
+ """§79 — machine-readable self-service contract. approval_required
72
+ stays True: paths propose changes, they never auto-mutate."""
73
+ return {
74
+ "capability": f"platform.path.{self.id}",
75
+ "version": self.version,
76
+ "use_case": self.use_case,
77
+ "inputs": self.inputs,
78
+ "policy": {"policies": self.policies,
79
+ "escape_hatches": self.escape_hatches},
80
+ "risk": {"severity": "medium",
81
+ "boundaries": ["scaffold", "pr"]},
82
+ "approval_required": True,
83
+ "mutates": False,
84
+ }
@@ -0,0 +1,107 @@
1
+ """CNCF Platform Engineering Maturity Model scoring — 5 aspects × 4 levels.
2
+
3
+ Evidence-driven: each aspect scores against declared signals (facts). A
4
+ level is claimed only when its signals are observed — the model never
5
+ asserts maturity without evidence ids.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ ASPECTS = ("investment", "adoption", "interfaces", "operations",
13
+ "measurement")
14
+ LEVELS = ("provisional", "operational", "scalable", "optimizing")
15
+
16
+ # aspect → level → signals (fact kinds/attrs observed). Conservative defaults.
17
+ SIGNALS = {
18
+ "investment": {
19
+ "operational": ["dedicated_team", "funded"],
20
+ "scalable": ["platform_team", "product_manager"],
21
+ "optimizing": ["measured_dora_impact", "continual_funding_review"],
22
+ },
23
+ "adoption": {
24
+ "operational": ["some_self_service", "documented_onboarding"],
25
+ "scalable": ["default_paths_adopted", "adoption_tracked"],
26
+ "optimizing": ["platform_is_default", "feedback_loops"],
27
+ },
28
+ "interfaces": {
29
+ "operational": ["catalog", "templates"],
30
+ "scalable": ["golden_paths", "self_service_api"],
31
+ "optimizing": ["composable_products", "versioned_interfaces"],
32
+ },
33
+ "operations": {
34
+ "operational": ["runbooks", "oncall"],
35
+ "scalable": ["slo_defined", "error_budgets", "gitops"],
36
+ "optimizing": ["policy_as_code", "automated_remediation"],
37
+ },
38
+ "measurement": {
39
+ "operational": ["metrics_collected"],
40
+ "scalable": ["dora_tracked", "slo_reported"],
41
+ "optimizing": ["outcome_metrics", "adoption_metrics", "finops"],
42
+ },
43
+ }
44
+
45
+ _ORDER = {"provisional": 0, "operational": 1, "scalable": 2, "optimizing": 3}
46
+
47
+
48
+ def _signal_provenance(v: Any) -> str:
49
+ """§80 — signals carry provenance: observed > declared > inferred.
50
+ A bare string is *declared*; {"signal": x, "provenance": p} carries it."""
51
+ if isinstance(v, dict):
52
+ return v.get("provenance", "declared")
53
+ return "declared"
54
+
55
+
56
+ def maturity(signals: dict[str, list[Any]],
57
+ evidence: dict[str, list[str]] | None = None) -> dict[str, Any]:
58
+ """§80 — Level = highest level whose signals are *observed*. Declared
59
+ signals produce a `declared_level` claim; the gap between observed and
60
+ declared is surfaced, never collapsed. Unknown stays unknown."""
61
+ aspects = {}
62
+ for aspect in ASPECTS:
63
+ raw = signals.get(aspect, [])
64
+ names = {(_signal_name(s)): _signal_provenance(s) for s in raw}
65
+ obs = {n for n, p in names.items() if p == "observed"}
66
+ dec = set(names) # declared ⊇ observed for level purposes
67
+ observed_level = _level_for(aspect, obs)
68
+ declared_level = _level_for(aspect, dec)
69
+ aspects[aspect] = {
70
+ "level": observed_level, # the evidence-backed claim
71
+ "declared_level": declared_level, # what configs/papers say
72
+ "overclaimed": _ORDER[declared_level] > _ORDER[observed_level],
73
+ "signals_observed": sorted(obs),
74
+ "signals_declared_only": sorted(dec - obs),
75
+ "evidence": (evidence or {}).get(aspect, [])}
76
+ return {"aspects": aspects, "schema": "platformforge/maturity/v2"}
77
+
78
+
79
+ def _signal_name(s: Any) -> str:
80
+ return s.get("signal", "") if isinstance(s, dict) else str(s)
81
+
82
+
83
+ def _level_for(aspect: str, have: set[str]) -> str:
84
+ level = "provisional"
85
+ for lv in ("operational", "scalable", "optimizing"):
86
+ want = SIGNALS[aspect].get(lv, [])
87
+ if all(s in have for s in want):
88
+ level = lv
89
+ else:
90
+ break # maturity compounds — stop at first unmet level
91
+ return level
92
+
93
+
94
+ def maturity_report(signals: dict[str, list[Any]],
95
+ evidence: dict[str, list[str]] | None = None
96
+ ) -> dict[str, Any]:
97
+ """Full v2 report: per-aspect + overall, with overclaim flags."""
98
+ out = maturity(signals, evidence)
99
+ overall = min(_ORDER[a["level"]] for a in out["aspects"].values())
100
+ out["overall_level"] = LEVELS[overall]
101
+ out["model"] = "CNCF Platform Engineering Maturity Model"
102
+ out["overclaimed_aspects"] = [a for a, v in out["aspects"].items()
103
+ if v["overclaimed"]]
104
+ out["note"] = ("level = highest fully-observed level; "
105
+ "declared_level may exceed it — that gap is the "
106
+ "missing evidence, not an error")
107
+ return out
@@ -0,0 +1,84 @@
1
+ """Scorecards v2 — §81 nine axes per entity. An axis with no evidence is
2
+ `unknown`, never zero: zero would mean "checked and failed", unknown means
3
+ "we don't know". Scores are evidence: each grade lists driving findings.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from collections import defaultdict
9
+ from typing import Any
10
+
11
+ _WEIGHT = {"info": 0, "low": 1, "medium": 3, "high": 7, "critical": 15}
12
+
13
+ # §81 axes — rule_id prefix/domain → axes the finding speaks to
14
+ AXES = ("ownership", "security", "reliability", "observability",
15
+ "supply_chain", "cost", "documentation", "deployment",
16
+ "operational_readiness")
17
+
18
+ _DOMAIN_AXES: dict[str, tuple[str, ...]] = {
19
+ "k8s": ("deployment", "reliability", "operational_readiness"),
20
+ "gitops": ("deployment", "operational_readiness"),
21
+ "cicd": ("deployment", "supply_chain"),
22
+ "security": ("security", "supply_chain"),
23
+ "iam": ("security",),
24
+ "secrets": ("security",),
25
+ "sbom": ("supply_chain",),
26
+ "supply": ("supply_chain",),
27
+ "finops": ("cost",),
28
+ "sre": ("reliability", "observability", "operational_readiness"),
29
+ "slo": ("reliability", "observability"),
30
+ "otel": ("observability",),
31
+ "iac": ("deployment", "security"),
32
+ "product": ("ownership", "documentation"),
33
+ "catalog": ("ownership", "documentation"),
34
+ }
35
+
36
+
37
+ def scorecard(findings: list[dict[str, Any]],
38
+ evidence_domains: set[str] | None = None) -> dict[str, Any]:
39
+ """§81 — per-entity, per-axis. `evidence_domains` names the domains
40
+ that produced facts (a domain with no findings but which *was*
41
+ measured is measurably clean, not unknown)."""
42
+ per: dict[str, dict[str, list[dict]]] = defaultdict(
43
+ lambda: defaultdict(list))
44
+ seen_domains: set[str] = set(evidence_domains or ())
45
+ for f in findings:
46
+ if f.get("status") != "violated":
47
+ continue
48
+ dom = (f.get("rule_id") or "").split("-")[1].lower() \
49
+ if "-" in (f.get("rule_id") or "") else ""
50
+ loc = f.get("location") or "unknown"
51
+ seen_domains.add(dom)
52
+ for axis in _DOMAIN_AXES.get(dom, ("operational_readiness",)):
53
+ per[loc][axis].append(f)
54
+
55
+ cards = {}
56
+ for loc, axes in sorted(per.items()):
57
+ ax = {}
58
+ for axis in AXES:
59
+ fs = axes.get(axis, [])
60
+ if not fs:
61
+ # unknown ≠ zero: report which domains could have produced
62
+ # evidence for this axis, and whether any was measured
63
+ producers = [d for d, a in _DOMAIN_AXES.items()
64
+ if axis in a]
65
+ ax[axis] = {
66
+ "status": "clean" if any(p in seen_domains
67
+ for p in producers)
68
+ else "unknown",
69
+ "score": None if not any(p in seen_domains
70
+ for p in producers) else 100}
71
+ continue
72
+ penalty = sum(_WEIGHT.get(f.get("severity", "info"), 0)
73
+ for f in fs)
74
+ score = max(0, 100 - penalty)
75
+ ax[axis] = {"status": "measured", "score": score,
76
+ "driving_findings": [f.get("rule_id") for f in fs]}
77
+ known = [a["score"] for a in ax.values() if a["score"] is not None]
78
+ overall = round(sum(known) / len(known)) if known else None
79
+ cards[loc] = {"axes": ax, "overall": overall,
80
+ "unknown_axes": [a for a in AXES
81
+ if ax[a]["status"] == "unknown"],
82
+ "total_violations": sum(len(v) for v in axes.values())}
83
+ return {"scorecards": cards, "count": len(cards), "axes": list(AXES),
84
+ "note": "unknown axis = no measured evidence, not a zero score"}
@@ -0,0 +1,36 @@
1
+ """Bundled data resolution — one contract for checkout and installed wheel.
2
+
3
+ Data directories (`rules/`, `knowledge/`, `contracts/`, `evals/`, `lab/`) live
4
+ at the repository root in development. In the wheel they are force-included
5
+ under ``platformforge/data/`` (see pyproject.toml), so an installed package is
6
+ self-contained: every loader resolves through :func:`data_path` and never
7
+ depends on the checkout being present.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from importlib import resources
13
+ from pathlib import Path
14
+
15
+
16
+ def data_path(*parts: str) -> Path:
17
+ """Resolve a bundled data path.
18
+
19
+ Order: ``platformforge/data/<parts>`` inside an installed wheel first,
20
+ then the repository root (development checkout / editable install where
21
+ no bundled ``data/`` tree exists inside the package).
22
+
23
+ ``platformforge/data/`` also exists in a checkout — it ships
24
+ ``portable_skills`` as regular package content — so the wheel probe
25
+ checks a force-included sentinel (``data/rules``) rather than ``data``
26
+ itself.
27
+ """
28
+ probe = parts if parts else ("rules",)
29
+ try:
30
+ pkg = resources.files("platformforge")
31
+ installed = pkg.joinpath("data", *probe)
32
+ if installed.is_dir() or installed.is_file():
33
+ return Path(str(pkg.joinpath("data", *parts)))
34
+ except (ModuleNotFoundError, TypeError, FileNotFoundError):
35
+ pass
36
+ return Path(__file__).resolve().parents[1].joinpath(*parts)
@@ -0,0 +1,5 @@
1
+ """Risk engine + criticality (prompt §130–131)."""
2
+
3
+ from platformforge.risk.engine import assess_change
4
+
5
+ __all__ = ["assess_change"]
@@ -0,0 +1,95 @@
1
+ """Change-risk assessment — §130.
2
+
3
+ Inputs are signals, each scored 0-3 and weighted. Output is a level with a
4
+ full decomposition — never a bare number. Criticality is taken from
5
+ *declared* signals (labels, owning findings, graph exposure); business
6
+ criticality is never inferred from technology alone (§131).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ LEVELS = ("low", "medium", "high", "critical")
14
+
15
+ # signal → (weight, scorer description)
16
+ SIGNALS = {
17
+ "blast_radius": 3, # # of impacted nodes per impact class
18
+ "criticality": 3, # declared criticality of touched nodes
19
+ "production": 2, # environment == production
20
+ "security_impact": 3, # security findings in blast radius
21
+ "identity_impact": 3, # IAM/principal changes
22
+ "network_exposure": 2, # new public/exposed surface
23
+ "data_persistence": 2, # touches db/bucket/volume
24
+ "availability_impact": 2, # touches workload/slo-bearing nodes
25
+ "cost_impact": 1, # billed delta detected
26
+ "reversibility": 2, # destructive/replacing change → 0
27
+ "test_coverage": 1, # tests/verification present → lowers
28
+ }
29
+
30
+ _THRESHOLDS = [(8, "low"), (16, "medium"), (26, "high"), (10**9, "critical")]
31
+
32
+
33
+ def _level(score: int) -> str:
34
+ for cap, name in _THRESHOLDS:
35
+ if score <= cap:
36
+ return name
37
+ return "critical"
38
+
39
+
40
+ def assess_change(signals: dict[str, Any]) -> dict[str, Any]:
41
+ """signals: dict of declared values; unknown signals are *unresolved*,
42
+ scored 0 and listed — never silently assumed safe."""
43
+ unknown = {k for k in signals if k not in SIGNALS}
44
+ decomposition: dict[str, dict[str, Any]] = {}
45
+ total = 0
46
+ for sig, weight in SIGNALS.items():
47
+ v = signals.get(sig)
48
+ if v is None:
49
+ decomposition[sig] = {"value": "unresolved", "score": 0,
50
+ "weight": weight}
51
+ continue
52
+ if isinstance(v, bool):
53
+ # True = protective for reversibility/test_coverage; risk for rest
54
+ raw = (0 if v else 3) if sig in ("reversibility", "test_coverage") \
55
+ else (3 if v else 0)
56
+ elif isinstance(v, (int, float)):
57
+ raw = min(3, max(0, int(v)))
58
+ else:
59
+ raw = {"none": 0, "low": 1, "medium": 2, "high": 3,
60
+ "critical": 3}.get(str(v).lower(), 0)
61
+ decomposition[sig] = {"value": v, "score": raw, "weight": weight}
62
+ total += raw * weight
63
+ unresolved = [s for s, d in decomposition.items()
64
+ if d["value"] == "unresolved"]
65
+ # §103 — unknown ≠ low risk: unresolved signals don't zero the level,
66
+ # they flag confidence and can only raise the reported level, never
67
+ # lower it. A "low" with unresolved signals is a claim, not a fact.
68
+ confidence = round(1.0 - len(unresolved) / len(SIGNALS), 3)
69
+ if unresolved and _level(total) in ("low", "medium"):
70
+ note = ("risk is underreported: unresolved signals could "
71
+ "raise the level")
72
+ else:
73
+ note = ("criticality is taken from declared signals only; "
74
+ "absence of a signal lowers confidence, not safety")
75
+ return {
76
+ "level": _level(total),
77
+ "score": total,
78
+ "confidence": confidence,
79
+ "underreported": bool(unresolved and _level(total) != "critical"),
80
+ "decomposition": decomposition,
81
+ "unknown_signals": sorted(unknown),
82
+ "unresolved": unresolved,
83
+ "note": note,
84
+ }
85
+
86
+
87
+ def criticality(node_attrs: dict[str, Any]) -> str:
88
+ """Declared criticality only — tier labels, never tech-inferred."""
89
+ for k in ("criticality", "tier", "criticality_tier"):
90
+ v = str(node_attrs.get(k) or "").lower()
91
+ if v in ("critical", "high", "medium", "low", "tier0", "tier1",
92
+ "tier2", "tier3"):
93
+ return {"tier0": "critical", "tier1": "high",
94
+ "tier2": "medium", "tier3": "low"}.get(v, v)
95
+ return "unresolved"
@@ -0,0 +1,49 @@
1
+ """Derive §130 risk signals from facts + graph — declared inputs only.
2
+
3
+ Every signal names its evidence. When evidence is absent the signal is
4
+ left unresolved rather than assumed safe."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import Any
9
+
10
+ from platformforge.risk.engine import criticality
11
+
12
+ _DATA_KINDS = {"database", "bucket", "volume", "cache", "queue"}
13
+ _AVAIL_KINDS = {"workload", "slo", "load_balancer", "ingress"}
14
+ _ID_KINDS = {"iam_principal", "role", "policy", "workload_identity",
15
+ "service_account"}
16
+ _NET_KINDS = {"load_balancer", "ingress", "dns", "gateway", "route"}
17
+
18
+
19
+ def signals_from_graph(graph: Any, node_ids: list[str],
20
+ blast: dict[str, Any] | None = None) -> dict[str, Any]:
21
+ """Map a blast-radius result + node attrs onto §130 signals."""
22
+ nodes = set(node_ids)
23
+ if blast:
24
+ for cls_nodes in blast.get("by_class", {}).values():
25
+ nodes.update(cls_nodes)
26
+ impacted = [graph.nodes[n] for n in nodes if n in graph.nodes] \
27
+ if hasattr(graph, "nodes") else []
28
+ crit = [criticality(n.attrs) for n in impacted]
29
+ kinds = {n.kind for n in impacted}
30
+ total = blast.get("impacted_total", 0) if blast else 0
31
+ sig: dict[str, Any] = {
32
+ "blast_radius": min(3, total // 3),
33
+ "criticality": ({"critical": 3, "high": 3, "medium": 2, "low": 1}
34
+ .get(next((c for c in crit if c != "unresolved"),
35
+ "unresolved"))
36
+ if impacted else None),
37
+ "security_impact": (bool(kinds & {"security_policy",
38
+ "admission_policy"})
39
+ if impacted else None),
40
+ "identity_impact": bool(kinds & _ID_KINDS) if impacted else None,
41
+ "network_exposure": bool(kinds & _NET_KINDS) if impacted else None,
42
+ "data_persistence": bool(kinds & _DATA_KINDS) if impacted else None,
43
+ "availability_impact": bool(kinds & _AVAIL_KINDS)
44
+ if impacted else None,
45
+ }
46
+ prod = [n for n in impacted
47
+ if str(n.attrs.get("env", "")).lower() in ("prod", "production")]
48
+ sig["production"] = bool(prod) if impacted else None
49
+ return sig