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,174 @@
1
+ """§64–§68 — false-positive / false-negative ledgers.
2
+
3
+ A ledger entry is a claim about a *finding* (rule verdict), never a
4
+ repo bug. Every reproducible failure carries a `regression_test`
5
+ pointer that must resolve to a case or test — the gate enforces it.
6
+ Precision is computed only when ground truth exists (§68).
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import yaml
15
+
16
+ ROOT_CAUSES = (
17
+ "missing-extractor", "bad-normalization", "bad-identity",
18
+ "bad-scope", "stale-knowledge", "rule-too-broad", "rule-too-narrow",
19
+ "graph-gap", "agent-reasoning", "routing-failure",
20
+ "context-omission", "provider-limitation",
21
+ )
22
+
23
+ LEDGER_DIR = Path(".platformforge/ledgers")
24
+
25
+ _REQUIRED = ("finding_id", "rule", "case", "root_cause", "regression_test")
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ class FalsePositiveRecord:
30
+ finding_id: str = ""
31
+ rule: str = ""
32
+ case: str = ""
33
+ why_false: str = ""
34
+ evidence: tuple[str, ...] = ()
35
+ root_cause: str = ""
36
+ fix: str = ""
37
+ regression_test: str = ""
38
+ status: str = "open" # open | fixed | wontfix
39
+
40
+ def to_dict(self) -> dict[str, Any]:
41
+ return {"schema": "platformforge/fp-record/v1",
42
+ "finding_id": self.finding_id, "rule": self.rule,
43
+ "case": self.case, "why_false": self.why_false,
44
+ "evidence": list(self.evidence),
45
+ "root_cause": self.root_cause, "fix": self.fix,
46
+ "regression_test": self.regression_test,
47
+ "status": self.status}
48
+
49
+
50
+ @dataclass(frozen=True)
51
+ class FalseNegativeRecord:
52
+ finding_id: str = "" # the verdict that should have fired
53
+ rule: str = ""
54
+ case: str = ""
55
+ missed_evidence: str = "" # what truth was present but unfound
56
+ root_cause: str = ""
57
+ fix: str = ""
58
+ regression_test: str = ""
59
+ status: str = "open"
60
+
61
+ def to_dict(self) -> dict[str, Any]:
62
+ return {"schema": "platformforge/fn-record/v1",
63
+ "finding_id": self.finding_id, "rule": self.rule,
64
+ "case": self.case,
65
+ "missed_evidence": self.missed_evidence,
66
+ "root_cause": self.root_cause, "fix": self.fix,
67
+ "regression_test": self.regression_test,
68
+ "status": self.status}
69
+
70
+
71
+ def _errors(d: dict[str, Any], kind: str) -> list[str]:
72
+ errs = [f"missing field: {k}" for k in _REQUIRED if not d.get(k)]
73
+ if d.get("root_cause") and d["root_cause"] not in ROOT_CAUSES:
74
+ errs.append(f"root_cause {d['root_cause']!r} not in {ROOT_CAUSES}")
75
+ if kind == "fp" and not d.get("why_false"):
76
+ errs.append("false-positive record without why_false")
77
+ if kind == "fn" and not d.get("missed_evidence"):
78
+ errs.append("false-negative record without missed_evidence")
79
+ return errs
80
+
81
+
82
+ def record_errors(d: dict[str, Any], kind: str) -> list[str]:
83
+ return _errors(d, kind)
84
+
85
+
86
+ def load_ledger(path: Path) -> list[dict[str, Any]]:
87
+ if not path.exists():
88
+ return []
89
+ doc = yaml.safe_load(path.read_text()) or {}
90
+ return doc.get("records", [])
91
+
92
+
93
+ def ledger_report(root: str | Path = LEDGER_DIR) -> dict[str, Any]:
94
+ """§66 — confirmed/refuted/unresolved + precision when truth exists."""
95
+ root = Path(root)
96
+ fp = load_ledger(root / "false-positives.yaml")
97
+ fn = load_ledger(root / "false-negatives.yaml")
98
+ errors = []
99
+ for d in fp:
100
+ errors += [f"fp:{d.get('finding_id', '?')}: {e}"
101
+ for e in _errors(d, "fp")]
102
+ for d in fn:
103
+ errors += [f"fn:{d.get('finding_id', '?')}: {e}"
104
+ for e in _errors(d, "fn")]
105
+ confirmed = sum(1 for d in fn if d.get("status") == "fixed") + \
106
+ sum(1 for d in fp if d.get("status") == "wontfix")
107
+ refuted = sum(1 for d in fp if d.get("status") == "fixed")
108
+ unresolved = sum(1 for d in fp + fn if d.get("status") == "open")
109
+ # §68 — precision only when a ground-truth corpus exists
110
+ truth = list(Path(".platformforge/cases/golden").glob("*/case.yaml")) + \
111
+ list(Path(".platformforge/cases/holdout").glob("*/case.yaml"))
112
+ precision = None
113
+ if truth:
114
+ # precision = confirmed findings / (confirmed + refuted findings)
115
+ denom = len(fp) + len(fn) or None
116
+ precision = ({"value": None, "records": 0,
117
+ "note": "no FP/FN records — precision "
118
+ "unmeasurable without findings to grade"}
119
+ if denom is None else
120
+ {"value": (None if denom < 3
121
+ else round(1 - (len(fp) / denom), 4)),
122
+ "records": denom,
123
+ "note": ("too few records for a meaningful estimate"
124
+ if denom < 3 else
125
+ "records-based estimate") +
126
+ f"; ground truth = {len(truth)} "
127
+ "replay cases"})
128
+ return {"schema": "platformforge/fp-fn-ledger/v1",
129
+ "false_positives": len(fp), "false_negatives": len(fn),
130
+ "confirmed": confirmed, "refuted": refuted,
131
+ "unresolved": unresolved, "precision": precision,
132
+ "errors": errors,
133
+ "verdict": "pass" if not errors else "fail"}
134
+
135
+
136
+ def validate_ledgers(root: str | Path = LEDGER_DIR) -> dict[str, Any]:
137
+ """Gate: every record is well-formed AND its regression_test resolves
138
+ to an existing case.yaml or test file."""
139
+ root = Path(root)
140
+ errors = []
141
+ for kind, f in (("fp", "false-positives.yaml"),
142
+ ("fn", "false-negatives.yaml")):
143
+ for d in load_ledger(root / f):
144
+ for e in _errors(d, kind):
145
+ errors.append(f"{kind}:{d.get('finding_id', '?')}: {e}")
146
+ rt = d.get("regression_test", "")
147
+ if rt and not _regression_resolves(rt):
148
+ errors.append(f"{kind}:{d.get('finding_id', '?')}: "
149
+ f"regression_test {rt!r} does not resolve")
150
+ return {"errors": errors, "verdict": "pass" if not errors else "fail"}
151
+
152
+
153
+ def _regression_resolves(ref: str) -> bool:
154
+ """A regression pointer resolves to a case dir, a test file (with an
155
+ optional ``::test_name`` selector that must exist in the file), or a
156
+ scenario dir — never to prose."""
157
+ file_part, _, selector = ref.partition("::")
158
+ p = Path(file_part)
159
+ found = p.exists() or any(
160
+ (Path(base) / file_part).exists()
161
+ for base in (".platformforge/cases/golden",
162
+ ".platformforge/cases/holdout",
163
+ "lab/scenarios", "tests"))
164
+ if not found:
165
+ return False
166
+ if selector:
167
+ target = p if p.is_file() else next(
168
+ (Path(base) / file_part for base in
169
+ (".platformforge/cases/golden", ".platformforge/cases/holdout",
170
+ "lab/scenarios", "tests")
171
+ if (Path(base) / file_part).is_file()), None)
172
+ if target is None or f"def {selector}(" not in target.read_text():
173
+ return False
174
+ return True
@@ -0,0 +1,142 @@
1
+ """§57–§62 — deterministic, offline replay.
2
+
3
+ Reuses lab/runner analyzer dispatch (fixture → facts → RuleEngine).
4
+ Grading: violated/absent rules + fact kinds vs expected_known_truth.
5
+ Output includes a canonical hash — replay twice, hashes must match.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import hashlib
10
+ import json
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ import yaml
16
+
17
+ from platformforge.cases.casefile import CaseFile, case_errors, discover_cases
18
+
19
+ SCHEMA = "platformforge/case-replay/v1"
20
+
21
+
22
+ def _fixture_dir(c: CaseFile) -> Path:
23
+ if c.fixture_from:
24
+ return Path(c.fixture_from) / "fixture"
25
+ return c.path.parent / "fixture"
26
+
27
+
28
+ def _run_case(c: CaseFile) -> dict[str, Any]:
29
+ """One deterministic pass: analyzers → facts → findings."""
30
+ from platformforge.lab.runner import CATALOG, _analyzer
31
+ from platformforge.models import Fact
32
+ from platformforge.rules import RuleEngine, load_catalog
33
+
34
+ fixture = _fixture_dir(c)
35
+ facts: list[dict[str, Any]] = []
36
+ errors = []
37
+ for dom in c.analyzers:
38
+ try:
39
+ facts += _analyzer(dom, fixture)["facts"]
40
+ except Exception as e: # noqa: BLE001 — analyzer failure is a case error
41
+ errors.append(f"{dom}: {e}")
42
+ rule_facts = [Fact.from_dict(f) for f in facts]
43
+ findings, skipped = RuleEngine(load_catalog(CATALOG)).evaluate(rule_facts)
44
+ violated = sorted({f.rule_id for f in findings if f.status == "violated"})
45
+ kinds = sorted({f.kind for f in rule_facts})
46
+ canonical = json.dumps({"violated": violated, "kinds": kinds,
47
+ "errors": sorted(errors)}, sort_keys=True)
48
+ return {"violated": violated, "kinds": kinds,
49
+ "errors": errors, "skipped_rules": len(skipped),
50
+ "hash": hashlib.sha256(canonical.encode()).hexdigest()}
51
+
52
+
53
+ def replay_case(c: CaseFile) -> dict[str, Any]:
54
+ errs = case_errors(c)
55
+ if errs:
56
+ return {"case": c.id, "verdict": "unresolved",
57
+ "errors": errs}
58
+ exp = c.expected_known_truth
59
+ t0 = time.time()
60
+ r1 = _run_case(c)
61
+ r2 = _run_case(c) # §62 — determinism: identical canonical result
62
+ deterministic = r1["hash"] == r2["hash"]
63
+
64
+ failures = []
65
+ want_v = set(exp.get("violated_rules", []))
66
+ want_absent = set(exp.get("absent_rules", []))
67
+ want_kinds = set(exp.get("fact_kinds", []))
68
+ got_v = set(r1["violated"])
69
+ got_k = set(r1["kinds"])
70
+ failures += [f"expected violated missing: {r}" for r in sorted(want_v - got_v)]
71
+ failures += [f"unexpected violation: {r}" for r in sorted(want_absent & got_v)]
72
+ failures += [f"expected fact kind missing: {k}"
73
+ for k in sorted(want_kinds - got_k)]
74
+ # FP/FN accounting vs declared truth (§65)
75
+ false_positives = sorted(got_v - want_v)
76
+ false_negatives = sorted(want_v - got_v)
77
+ if not deterministic:
78
+ failures.append("nondeterministic replay: hash mismatch")
79
+ verdict = "pass" if not failures and not r1["errors"] else "fail"
80
+ return {"schema": SCHEMA, "case": c.id, "tier": c.tier,
81
+ "classification": c.classification,
82
+ "truth_class": c.truth_class,
83
+ "verdict": verdict, "deterministic": deterministic,
84
+ "failures": failures, "analyzer_errors": r1["errors"],
85
+ "violated": r1["violated"], "fact_kinds": r1["kinds"],
86
+ "false_positives": false_positives,
87
+ "false_negatives": false_negatives,
88
+ "result_hash": r1["hash"], "ms": round((time.time() - t0) * 1000, 1)}
89
+
90
+
91
+ def replay_all(root: str | Path = ".platformforge/cases",
92
+ tier: str | None = None) -> dict[str, Any]:
93
+ cases = discover_cases(root)
94
+ if tier:
95
+ cases = [c for c in cases if c.tier == tier]
96
+ results = [replay_case(c) for c in cases]
97
+ by_tier: dict[str, dict[str, int]] = {}
98
+ for r in results:
99
+ t = by_tier.setdefault(r["tier"], {"pass": 0, "fail": 0,
100
+ "unresolved": 0, "total": 0})
101
+ t[r["verdict"]] = t.get(r["verdict"], 0) + 1
102
+ t["total"] += 1
103
+ failed = [r["case"] for r in results if r["verdict"] == "fail"]
104
+ corpus_hash = hashlib.sha256(json.dumps(
105
+ [(r["case"], r.get("result_hash", "")) for r in results],
106
+ sort_keys=True).encode()).hexdigest()
107
+ return {"schema": "platformforge/case-corpus/v1",
108
+ "cases": results, "by_tier": by_tier,
109
+ "corpus_hash": corpus_hash,
110
+ "verdict": "pass" if not failed and results else "fail",
111
+ "failed": failed,
112
+ "note": "golden = development corpus; holdout = never tuned "
113
+ "against — report both, merge neither"}
114
+
115
+
116
+ def validate_corpus(root: str | Path = ".platformforge/cases"
117
+ ) -> dict[str, Any]:
118
+ """Structure-only check — every case.yaml satisfies §54."""
119
+ cases = discover_cases(root)
120
+ bad = {c.id: case_errors(c) for c in cases}
121
+ bad = {k: v for k, v in bad.items() if v}
122
+ return {"cases": len(cases), "invalid": bad,
123
+ "verdict": "pass" if cases and not bad else "fail"}
124
+
125
+
126
+ def write_case_template(path: str | Path) -> None:
127
+ Path(path).write_text(yaml.safe_dump({
128
+ "id": "case-<n>",
129
+ "classification": "fixture-derived",
130
+ "tier": "golden",
131
+ "truth_class": "known-bad",
132
+ "task": "<one-line task the operator ran>",
133
+ "scope": "<repo/platform slice>",
134
+ "analyzers": ["k8s"],
135
+ "fixture_from": "lab/scenarios/<id>",
136
+ "expected_known_truth": {
137
+ "violated_rules": [], "absent_rules": [], "fact_kinds": []},
138
+ "observations": [],
139
+ "agent_route": {"selected": [], "rejected": [], "fanout": 0},
140
+ "context_cost": {"input_bytes": 0, "relevant_bytes": 0},
141
+ "verdict": "unresolved",
142
+ }, sort_keys=False, allow_unicode=True))
@@ -0,0 +1,162 @@
1
+ """§69–§84 — agent dogfooding: per-case route decisions, over/under-
2
+ routing detection, champion/challenger shadow benchmark.
3
+
4
+ Case-derived signals are inferred honestly: domains = analyzers;
5
+ security_sensitive = security-family analyzers; complexity from domain
6
+ count. The router's own output is the evidence — nothing is assumed.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from platformforge.cases.casefile import CaseFile, discover_cases
16
+
17
+ SECURITY_DOMAINS = {"security", "iam", "secrets", "supply", "sbom",
18
+ "slsa", "cosign", "policy"}
19
+ # domain keys in routing.DOMAIN_SPECIALIST vocabulary
20
+ _DOMAIN_MAP = {"cloud-aws": "aws", "cloud-gcp": "cloud",
21
+ "iac": "terraform", "secrets": "security",
22
+ "iam": "security", "supply": "security",
23
+ "sbom": "security", "slsa": "security",
24
+ "cosign": "security", "slo": "sre"}
25
+
26
+
27
+ def signal_for_case(c: CaseFile):
28
+ from platformforge.routing.router import TaskSignal
29
+ domains = [_DOMAIN_MAP.get(d, d) for d in c.analyzers]
30
+ return TaskSignal(
31
+ task_type="analysis",
32
+ domains=domains,
33
+ complexity="high" if len(domains) >= 3 else (
34
+ "medium" if len(domains) == 2 else "low"),
35
+ security_sensitive=bool(SECURITY_DOMAINS & set(c.analyzers)),
36
+ evidence_available=True,
37
+ freshness="current",
38
+ )
39
+
40
+
41
+ def audit_case(c: CaseFile) -> dict[str, Any]:
42
+ from platformforge.routing.router import route
43
+ sig = signal_for_case(c)
44
+ t0 = time.time()
45
+ d = route(sig)
46
+ agents = d["agents"]
47
+ flags: list[str] = []
48
+ # over-routing (§71): one specialist would do, but a crowd was called
49
+ if len(c.analyzers) == 1 and len(d["specialists"]) > 2:
50
+ flags.append("over-routing: single-domain case dispatched "
51
+ f"{len(d['specialists'])} specialists")
52
+ if d["mode"] == "coordinated" and len(c.analyzers) == 1 \
53
+ and not sig.security_sensitive:
54
+ flags.append("over-routing: coordinated mode for a "
55
+ "single-domain case")
56
+ # under-routing (§72): security-sensitive without security reviewer
57
+ if sig.security_sensitive and \
58
+ "platform-security-reviewer" not in d["reviewers"]:
59
+ flags.append("under-routing: security-sensitive case has no "
60
+ "security reviewer")
61
+ # every agentic route must end in independent verification
62
+ if d["mode"] != "deterministic" and not d["verifier"]:
63
+ flags.append("missing verifier on an agentic route")
64
+ return {
65
+ "schema": "platformforge/route-decision/v1",
66
+ "case": c.id, "tier": c.tier,
67
+ "router_input": {k: getattr(sig, k)
68
+ for k in sig.__dataclass_fields__},
69
+ "mode": d["mode"], "budget": d["budget"],
70
+ "selected": agents, "specialists": d["specialists"],
71
+ "reviewers": d["reviewers"], "coordinator": d["coordinator"],
72
+ "verifier": d["verifier"],
73
+ "rejected": sorted(set(_all_candidate_names()) - set(agents)),
74
+ "fanout": len(agents), "dag_stages": len(d["dag"]),
75
+ "reasons": d["reasons"], "flags": flags,
76
+ "ms": round((time.time() - t0) * 1000, 1),
77
+ "result": "flagged" if flags else "ok"}
78
+
79
+
80
+ def _all_candidate_names() -> list[str]:
81
+ from platformforge.agents.roster import AGENTS
82
+ return list(AGENTS)
83
+
84
+
85
+ def audit_corpus(root: str | Path = ".platformforge/cases",
86
+ ledger: bool = True) -> dict[str, Any]:
87
+ cases = discover_cases(root)
88
+ decisions = [audit_case(c) for c in cases]
89
+ flagged = [d["case"] for d in decisions if d["flags"]]
90
+ by_mode: dict[str, int] = {}
91
+ fanout = 0
92
+ for d in decisions:
93
+ by_mode[d["mode"]] = by_mode.get(d["mode"], 0) + 1
94
+ fanout += d["fanout"]
95
+ out = {"schema": "platformforge/routing-audit/v1",
96
+ "cases": len(decisions), "decisions": decisions,
97
+ "by_mode": by_mode,
98
+ "avg_fanout": round(fanout / len(decisions), 2) if decisions else 0,
99
+ "flagged": flagged,
100
+ "verdict": "pass" if not flagged else "flagged",
101
+ "note": "flags are review signals, not auto-fixes — the "
102
+ "router is not self-modified (§76)"}
103
+ if ledger:
104
+ p = Path(".platformforge/ledger")
105
+ p.mkdir(parents=True, exist_ok=True)
106
+ with open(p / "routing-decisions.jsonl", "w") as fh:
107
+ for d in decisions:
108
+ fh.write(json.dumps(d, default=str) + "\n")
109
+ out["ledger"] = str(p / "routing-decisions.jsonl")
110
+ return out
111
+
112
+
113
+ def champion_challenger(root: str | Path = ".platformforge/cases",
114
+ challenger: str | Path | None = None
115
+ ) -> dict[str, Any]:
116
+ """§77 — champion = rules/catalog/routing.yaml; challenger = an
117
+ alternate table run in offline shadow mode. Scores per case:
118
+ correctness (right specialist for the domains), evidence recall,
119
+ agent count, projected cost, latency, tool calls (router-side).
120
+ Without a challenger file this reports champion-only shadow."""
121
+ from platformforge.routing.router import route
122
+
123
+ cases = discover_cases(root)
124
+ rows = []
125
+ for c in cases:
126
+ sig = signal_for_case(c)
127
+ champ = route(sig)
128
+ row = {"case": c.id, "champion": _score(c, sig, champ)}
129
+ if challenger and Path(challenger).is_file():
130
+ chal = route(sig, routes_path=challenger)
131
+ row["challenger"] = _score(c, sig, chal)
132
+ row["delta"] = _delta(row["champion"], row["challenger"])
133
+ rows.append(row)
134
+ return {"schema": "platformforge/routing-bench/v1",
135
+ "cases": rows, "challenger": str(challenger) if challenger
136
+ else "none — champion-only shadow",
137
+ "note": "challenger never activates silently — routing "
138
+ "changes require review (§77)"}
139
+
140
+
141
+ def _score(c: CaseFile, sig, d: dict[str, Any]) -> dict[str, Any]:
142
+ """Offline score — correctness = every case domain has its
143
+ specialist selected; recall = specialists / needed."""
144
+ from platformforge.routing.router import DOMAIN_SPECIALIST
145
+ needed = {DOMAIN_SPECIALIST[dom] for dom in
146
+ {_DOMAIN_MAP.get(a, a) for a in c.analyzers}
147
+ if dom in DOMAIN_SPECIALIST}
148
+ selected = set(d["specialists"])
149
+ recall = len(needed & selected) / len(needed) if needed else 1.0
150
+ return {"mode": d["mode"], "agents": len(d["agents"]),
151
+ "specialists": len(d["specialists"]),
152
+ "correctness": sorted(needed <= selected and ["full"]
153
+ or [s for s in needed - selected]),
154
+ "evidence_recall": round(recall, 3),
155
+ "budget": d["budget"], "dag_stages": len(d["dag"])}
156
+
157
+
158
+ def _delta(champ: dict, chal: dict) -> dict[str, Any]:
159
+ return {"agents": chal["agents"] - champ["agents"],
160
+ "evidence_recall": round(chal["evidence_recall"]
161
+ - champ["evidence_recall"], 3),
162
+ "mode": f"{champ['mode']}→{chal['mode']}"}
@@ -0,0 +1,11 @@
1
+ """Caveman — first-party output compression.
2
+
3
+ Reduce language, never substance. Protected spans (commands, paths, ids,
4
+ exceptions, warnings, IAM actions, ARNs, URLs, evidence, fact/rule ids,
5
+ numbers, units, versions) are never compressed away. Every compression emits
6
+ a receipt with before/after measurements.
7
+ """
8
+
9
+ from platformforge.caveman.compress import CompressionReceipt, compress
10
+
11
+ __all__ = ["CompressionReceipt", "compress"]
@@ -0,0 +1,156 @@
1
+ """Output compression with protected spans + receipts.
2
+
3
+ Modes: off | lite | full | auto.
4
+ off — passthrough
5
+ lite — drop filler, keep sentences & structure
6
+ full — aggressive: terse fragments, bullets over prose
7
+ auto — decide by context risk (info→full, architecture→lite,
8
+ incident/destructive→prose with explicit warnings)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import re
14
+ from dataclasses import asdict, dataclass, field
15
+ from typing import Any
16
+
17
+ from platformforge.tokensave.estimate import estimate_tokens
18
+
19
+ PROTECTED_PATTERNS = [
20
+ r"\b(error|warning|exception|denied|failed|fatal|panic)[:\s][^\n]*",
21
+ r"PF-[A-Z0-9-]+-\d+", # fact/rule ids
22
+ r"artifact://sha256/[0-9a-f]{64}",
23
+ r"arn:aws:[\w\-:]+/[^\s]*", # ARNs
24
+ r"https?://[^\s)\]]+",
25
+ r"[\w./\-]+/[\w./\-]+", # paths
26
+ r"\bv?\d+\.\d+(?:\.\d+)?\b", # versions
27
+ r"\b\d+(?:\.\d+)?\s*(?:ms|s|MB|GB|TB|%|Mi|Gi|m)?\b",
28
+ r"`[^`]+`", # inline code/commands
29
+ r"\b(allow|deny|delete|create|update|iam:[\w*]+|s3:[\w*]+)\b",
30
+ r"AKIA[0-9A-Z]{16}|REDACTED:[\w]+:[0-9a-f]{8}",
31
+ ]
32
+
33
+ FILLER = re.compile(
34
+ r"(?i)\b(certainly|basically|actually|essentially|in conclusion|"
35
+ r"it is important to note that|please note that|as you can see|"
36
+ r"as mentioned (earlier|above)|in order to|due to the fact that|"
37
+ r"it should be noted|the fact that)\b[,]?")
38
+ WORDY = [
39
+ (re.compile(r"(?i)\bis able to\b"), "can"),
40
+ (re.compile(r"(?i)\bin the event that\b"), "if"),
41
+ (re.compile(r"(?i)\ba large number of\b"), "many"),
42
+ (re.compile(r"(?i)\bat this point in time\b"), "now"),
43
+ (re.compile(r"(?i)\bhas the ability to\b"), "can"),
44
+ (re.compile(r"(?i)\bprior to\b"), "before"),
45
+ (re.compile(r"(?i)\bin close proximity to\b"), "near"),
46
+ (re.compile(r"(?i)\bdo not hesitate to\b"), ""),
47
+ ]
48
+ SENT_SPLIT = re.compile(r"(?<=[.!?])\s+")
49
+ RISKY = re.compile(r"(?i)\b(destroy|delete|drop|production|incident|sev[0-9]|"
50
+ r"irreversible|destructive|security breach|outage)\b")
51
+ DECISION = re.compile(r"(?i)\b(architecture|design decision|tradeoff|trade-off|"
52
+ r"alternatives? considered|rationale)\b")
53
+
54
+
55
+ @dataclass
56
+ class CompressionReceipt:
57
+ mode: str
58
+ before_chars: int
59
+ after_chars: int
60
+ before_tokens_est: int
61
+ after_tokens_est: int
62
+ compression_ratio: float
63
+ protected_spans: int
64
+ detail: str = ""
65
+ extra: dict[str, Any] = field(default_factory=dict)
66
+
67
+ def to_dict(self) -> dict[str, Any]:
68
+ return asdict(self)
69
+
70
+
71
+ _COMBINED = re.compile(
72
+ "|".join(f"(?:{p})" for p in PROTECTED_PATTERNS), re.IGNORECASE)
73
+
74
+
75
+ def _protect(text: str) -> tuple[str, list[str]]:
76
+ """Replace protected spans with placeholders → compress prose only.
77
+ Single alternation pass: a placeholder can never match a later pattern."""
78
+ spans: list[str] = []
79
+
80
+ def stash(m: re.Match) -> str:
81
+ spans.append(m.group(0))
82
+ return f"\x00{len(spans) - 1}\x00"
83
+
84
+ return _COMBINED.sub(stash, text), spans
85
+
86
+
87
+ def _restore(text: str, spans: list[str]) -> str:
88
+ return re.sub(r"\x00(\d+)\x00", lambda m: spans[int(m.group(1))], text)
89
+
90
+
91
+ def _lite(text: str) -> str:
92
+ text = FILLER.sub("", text)
93
+ for rx, rep in WORDY:
94
+ text = rx.sub(rep, text)
95
+ text = re.sub(r"[ \t]{2,}", " ", text)
96
+ return re.sub(r"\n{3,}", "\n\n", text).strip()
97
+
98
+
99
+ def _full(text: str) -> str:
100
+ text = _lite(text)
101
+ out = []
102
+ for sent in SENT_SPLIT.split(text):
103
+ s = sent.strip()
104
+ if not s:
105
+ continue
106
+ # drop connective-only sentences, keep payload sentences
107
+ if len(s) < 4:
108
+ continue
109
+ if re.match(r"(?i)^(however|moreover|furthermore|additionally|also|"
110
+ r"in addition|note that|it is worth)\b[,]?\s*", s):
111
+ s = re.sub(r"(?i)^(however|moreover|furthermore|additionally|also|"
112
+ r"in addition|note that|it is worth)\b[,]?\s*", "", s)
113
+ out.append(s)
114
+ return "\n".join(out)
115
+
116
+
117
+ def choose_mode(text: str, context_risk: str = "normal") -> str:
118
+ if context_risk in ("security-incident", "destructive"):
119
+ return "off" # prose + explicit warnings — no compression on risk
120
+ if RISKY.search(text):
121
+ return "lite" # keep signal fully readable
122
+ if DECISION.search(text):
123
+ return "lite"
124
+ return "full"
125
+
126
+
127
+ def compress(text: str, mode: str = "lite",
128
+ context_risk: str = "normal") -> tuple[str, CompressionReceipt]:
129
+ if mode not in ("off", "lite", "full", "auto"):
130
+ raise ValueError(f"unknown mode {mode!r}")
131
+ # §16 — secrets never cross the compression boundary, mode "off"
132
+ # included. Redact before anything else; the receipt records counts.
133
+ from platformforge.core.redaction import redact_text_report, redaction_receipt
134
+ text, redactions = redact_text_report(text)
135
+ actual = choose_mode(text, context_risk) if mode == "auto" else mode
136
+ before_chars = len(text)
137
+ before_tokens = estimate_tokens(text)
138
+
139
+ protected, spans = _protect(text)
140
+ if actual == "off":
141
+ out = text
142
+ elif actual == "lite":
143
+ out = _lite(protected)
144
+ else:
145
+ out = _full(protected)
146
+ out = _restore(out, spans)
147
+
148
+ rc = CompressionReceipt(
149
+ mode=actual, before_chars=before_chars, after_chars=len(out),
150
+ before_tokens_est=before_tokens, after_tokens_est=estimate_tokens(out),
151
+ compression_ratio=(1 - len(out) / before_chars) if before_chars else 0.0,
152
+ protected_spans=len(spans),
153
+ )
154
+ if redactions:
155
+ rc.extra["redaction"] = redaction_receipt("caveman", redactions)
156
+ return out, rc
@@ -0,0 +1,16 @@
1
+ """GitOps & CI/CD analysis: ArgoCD, Flux, GitHub Actions, GitLab CI,
2
+ Dockerfile, Backstage catalog entities."""
3
+
4
+ from platformforge.cicd.backstage import analyze_backstage
5
+ from platformforge.cicd.dockerfile import analyze_dockerfile
6
+ from platformforge.cicd.github_actions import analyze_gha
7
+ from platformforge.cicd.gitlab_ci import analyze_gitlab_ci
8
+ from platformforge.cicd.gitops import analyze_gitops
9
+
10
+ __all__ = [
11
+ "analyze_backstage",
12
+ "analyze_dockerfile",
13
+ "analyze_gha",
14
+ "analyze_gitlab_ci",
15
+ "analyze_gitops",
16
+ ]