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,254 @@
1
+ """Artifact-type detection + dispatch for `collect`.
2
+
3
+ Signals are intentionally cheap and deterministic: filename patterns first,
4
+ then document shape. Every file under the collected path is accounted for —
5
+ `detected` or `undetected` — so nothing is silently dropped.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ import yaml
15
+
16
+ _IGNORE = {".git", ".platformforge", ".venv", "node_modules",
17
+ "__pycache__", ".pytest-tmp", ".pytest_cache"}
18
+
19
+
20
+ def _looks_k8s(doc: dict) -> bool:
21
+ return isinstance(doc.get("apiVersion"), str) and "kind" in doc
22
+
23
+
24
+ def _shape_domain(path: Path, doc: Any) -> str | None:
25
+ """Document-shape heuristics, ordered most-specific first."""
26
+ if isinstance(doc, dict):
27
+ if _looks_k8s(doc):
28
+ if doc.get("kind") in ("Application", "AppProject",
29
+ "ApplicationSet") \
30
+ and "argoproj.io" in str(doc.get("apiVersion")):
31
+ return "gitops"
32
+ if "toolkit.fluxcd.io" in str(doc.get("apiVersion")):
33
+ return "gitops"
34
+ api = str(doc.get("apiVersion", ""))
35
+ if "kyverno.io" in api or "policies.kyverno.io" in api or \
36
+ "gatekeeper.sh" in api or \
37
+ "admissionregistration.k8s.io" in api:
38
+ return "kyverno"
39
+ return "k8s"
40
+ if doc.get("format_version") or "terraform_version" in doc:
41
+ return "plan" if "planned_values" in doc else "state"
42
+ # AWS CLI dumps: top-level keys like Vpcs/Subnets/Roles/Accounts
43
+ if set(doc) & {"Vpcs", "Subnets", "InternetGateways", "NatGateways",
44
+ "RouteTables", "SecurityGroups", "LoadBalancers",
45
+ "HostedZones", "Roles", "Policies", "Users",
46
+ "PolicyVersion", "Organization", "Accounts",
47
+ "OrganizationalUnits", "Reservations", "Functions",
48
+ "Clusters", "Buckets", "DBInstances", "QueueUrls",
49
+ "Queues", "Topics", "TableNames"}:
50
+ return "cloud-aws"
51
+ if "flow" in doc and isinstance(doc.get("flow"), dict):
52
+ return "hubble"
53
+ if "Statement" in doc:
54
+ return "iam"
55
+ if doc.get("bomFormat") == "CycloneDX" \
56
+ or doc.get("spdxVersion"):
57
+ return "sbom"
58
+ if "predicateType" in doc or "provenance" in doc:
59
+ return "supply"
60
+ if any(k in doc for k in ("BillingAccountId", "ResourceId",
61
+ "ServiceName")) and "Charge" in str(doc)[:2000]:
62
+ return "finops"
63
+ # billing exports in JSON: OpenCost/Kubecost/CUR-as-JSON shapes
64
+ first_row = None
65
+ for key in ("rows", "data", "results", "items"):
66
+ if isinstance(doc.get(key), list) and doc[key]:
67
+ first_row = doc[key][0]
68
+ break
69
+ probe = first_row if isinstance(first_row, dict) else doc
70
+ if isinstance(probe, dict) and (
71
+ any(isinstance(k, str) and k.startswith("lineItem/")
72
+ for k in probe)
73
+ or "UnblendedCost" in probe or "PreTaxCost" in probe
74
+ or "CostInBillingCurrency" in probe
75
+ or "totalCost" in probe
76
+ or ("cost" in probe and ("usage" in probe
77
+ or "labels" in probe))):
78
+ return "finops"
79
+ if isinstance(doc, dict) and isinstance(doc.get("items"), list) \
80
+ and all(_looks_k8s(i) for i in doc["items"]
81
+ if isinstance(i, dict)):
82
+ return "k8s"
83
+ return None
84
+
85
+
86
+ def detect_file(path: Path) -> str | None:
87
+ """Best-effort domain for a single artifact file."""
88
+ name = path.name.lower()
89
+ suffix = path.suffix.lower()
90
+ parts = {p.lower() for p in path.parts}
91
+ # path-shaped artifacts first — filename is the contract
92
+ if {".github", "workflows"} <= parts and suffix in (".yml", ".yaml"):
93
+ return "cicd-gha"
94
+ if name == ".gitlab-ci.yml":
95
+ return "gitlab-ci"
96
+ if name.startswith("dockerfile") or name.endswith(".dockerfile"):
97
+ return "dockerfile"
98
+ if name.startswith("catalog-info.") and suffix in (".yaml", ".yml"):
99
+ return "backstage"
100
+ if suffix == ".tf":
101
+ return "iac"
102
+ if name == "chart.yaml":
103
+ return "helm"
104
+ if name in ("kustomization.yaml", "kustomization.yml"):
105
+ return "kustomize"
106
+ if "hubble" in name or "flows" in name:
107
+ return "hubble"
108
+ if name in ("policy.json", "trust.json") or name.endswith(".policy.json"):
109
+ return "iam"
110
+ if name in ("sbom.json", "sbom.cdx.json") or "sbom" in name:
111
+ return "sbom"
112
+ if name in ("billing.json", "costs.json", "focus.csv", "billing.csv") \
113
+ or "billing" in name:
114
+ return "finops"
115
+ if name in ("supply.json", "provenance.json", "attestation.json") \
116
+ or "in-toto" in name:
117
+ return "supply"
118
+ if "sigstore" in name or "cosign" in name \
119
+ or name.endswith((".sig", ".bundle")):
120
+ return "cosign"
121
+ if name in ("slo.yaml", "slo.yml"):
122
+ return "slo"
123
+ if name in ("otel.yaml", "otel.yml") or "otel" in name:
124
+ return "otel"
125
+ if suffix == ".csv":
126
+ # billing exports by header shape, not filename
127
+ try:
128
+ header = path.open().readline()
129
+ except OSError:
130
+ return None
131
+ if "lineItem/" in header or "UnblendedCost" in header or \
132
+ "PreTaxCost" in header or "CostInBillingCurrency" in header \
133
+ or ("cost" in header.lower() and
134
+ ("sku" in header.lower() or "usage" in header.lower())):
135
+ return "finops"
136
+ return None
137
+ if suffix not in (".json", ".yaml", ".yml"):
138
+ return None
139
+ try:
140
+ doc = json.loads(path.read_text()) if suffix == ".json" \
141
+ else yaml.safe_load(path.read_text())
142
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError,
143
+ yaml.YAMLError):
144
+ return None
145
+ if isinstance(doc, list):
146
+ doc = next((d for d in doc if isinstance(d, dict)), None)
147
+ if doc is None:
148
+ return None
149
+ return _shape_domain(path, doc)
150
+
151
+
152
+ def collect(path: str | Path) -> dict[str, Any]:
153
+ """Walk path, detect artifact types, run matching analyzers, merge facts.
154
+
155
+ Returns {facts, detected: {domain: [files]}, undetected: [files]}.
156
+ Read-only: this never touches a provider.
157
+ """
158
+ root = Path(path)
159
+ files = ([root] if root.is_file() else
160
+ sorted(p for p in root.rglob("*")
161
+ if p.is_file() and not _IGNORE.intersection(p.parts)))
162
+ detected: dict[str, list[str]] = {}
163
+ undetected: list[str] = []
164
+ buckets: dict[str, list[Path]] = {}
165
+ for f in files:
166
+ dom = detect_file(f)
167
+ if dom is None:
168
+ undetected.append(str(f))
169
+ else:
170
+ detected.setdefault(dom, []).append(str(f))
171
+ buckets.setdefault(dom, []).append(f)
172
+
173
+ facts: list[dict[str, Any]] = []
174
+ from platformforge import iac, security
175
+ from platformforge.finops import cost_facts
176
+ for dom, paths in buckets.items():
177
+ try:
178
+ if dom == "iac":
179
+ facts += iac.analyze_hcl(paths[0].parent)["facts"]
180
+ elif dom in ("plan", "state"):
181
+ fn = (iac.analyze_plan if dom == "plan" else
182
+ iac.analyze_state)
183
+ for p in paths:
184
+ facts += fn(str(p))["facts"]
185
+ elif dom == "iam":
186
+ for p in paths:
187
+ facts += security.analyze_iam_policy(str(p))["facts"]
188
+ elif dom == "sbom":
189
+ facts += security.analyze_sbom(str(paths[0]))["facts"]
190
+ elif dom == "supply":
191
+ facts += security.analyze_supply(str(paths[0]))["facts"]
192
+ elif dom == "finops":
193
+ from platformforge.finops.costs import cost_facts_from_rows
194
+ from platformforge.finops.ingest import ingest_billing
195
+ ing = ingest_billing(str(paths[0]))
196
+ if ing["rows"]:
197
+ facts += cost_facts_from_rows(
198
+ ing["rows"], source=str(paths[0]))["facts"]
199
+ else:
200
+ facts += cost_facts(str(paths[0]))["facts"]
201
+ elif dom == "cloud-aws":
202
+ from platformforge.cloud import analyze_aws_dump
203
+ for p in paths:
204
+ facts += analyze_aws_dump(str(p))["facts"]
205
+ elif dom == "helm":
206
+ from platformforge.k8s.helm import analyze_helm
207
+ parent = paths[0].parent
208
+ facts += analyze_helm(parent)["facts"]
209
+ elif dom == "kustomize":
210
+ from platformforge.k8s.helm import analyze_kustomize
211
+ facts += analyze_kustomize(paths[0].parent)["facts"]
212
+ elif dom == "hubble":
213
+ from platformforge.k8s.hubble import analyze_hubble
214
+ for p in paths:
215
+ facts += analyze_hubble(str(p))["facts"]
216
+ elif dom == "kyverno":
217
+ from platformforge.security.kyverno import analyze_kyverno
218
+ parent = paths[0].parent
219
+ facts += analyze_kyverno(parent)["facts"]
220
+ elif dom == "cosign":
221
+ from platformforge.security.cosign import analyze_cosign
222
+ for p in paths:
223
+ facts += analyze_cosign(str(p))["facts"]
224
+ elif dom in ("k8s", "gitops"):
225
+ # dir-level domains: run once over the common parent
226
+ parent = paths[0].parent
227
+ mod = ("platformforge.k8s:analyze_k8s" if dom == "k8s"
228
+ else "platformforge.cicd:analyze_gitops")
229
+ m, fn = mod.split(":")
230
+ import importlib
231
+ facts += getattr(importlib.import_module(m), fn)(parent)["facts"]
232
+ elif dom == "cicd-gha":
233
+ from platformforge.cicd import analyze_gha
234
+ for p in paths:
235
+ facts += analyze_gha(p)["facts"]
236
+ elif dom == "gitlab-ci":
237
+ from platformforge.cicd import analyze_gitlab_ci
238
+ for p in paths:
239
+ facts += analyze_gitlab_ci(p)["facts"]
240
+ elif dom == "dockerfile":
241
+ from platformforge.cicd import analyze_dockerfile
242
+ for p in paths:
243
+ facts += analyze_dockerfile(p)["facts"]
244
+ elif dom == "backstage":
245
+ from platformforge.cicd import analyze_backstage
246
+ for p in paths:
247
+ facts += analyze_backstage(p)["facts"]
248
+ except Exception as exc: # noqa: BLE001 — analyzer failure degrades to undetected, not a crash
249
+ undetected.append(f"{dom}:{exc!r}")
250
+ # secrets is a baseline dir-scan over everything collected — dumps are
251
+ # exactly where leaked credentials surface
252
+ from platformforge.security import scan_secrets
253
+ facts += scan_secrets(root)["facts"]
254
+ return {"facts": facts, "detected": detected, "undetected": undetected}
@@ -0,0 +1,10 @@
1
+ """Unified Context Gateway (§56–75) — single layer through which all
2
+ model/agent context passes."""
3
+
4
+ from platformforge.context.capsule import ContextCapsule
5
+ from platformforge.context.gateway import ContextGateway, ContextRequest
6
+ from platformforge.context.refs import ContextRef
7
+ from platformforge.context.sufficiency import ContextSufficiencyResult, Sufficiency
8
+
9
+ __all__ = ["ContextCapsule", "ContextGateway", "ContextRef",
10
+ "ContextRequest", "ContextSufficiencyResult", "Sufficiency"]
@@ -0,0 +1,60 @@
1
+ """ContextCapsule (§60–61) — the bounded payload handed to a model/agent.
2
+
3
+ Sections hold refs, not duplicated bytes (§62): facts/findings/rules are
4
+ `fact://`/`finding://` pointers into the store; expandable detail lives
5
+ behind `context://` refs resolved by `context expand` (§63–64)."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from dataclasses import asdict, dataclass, field
11
+ from typing import Any
12
+
13
+ SCHEMA = "platformforge/context-capsule/v1"
14
+
15
+ SCOPES = ("repository", "resource", "cluster", "fleet", "incident",
16
+ "operation", "service") # §68
17
+
18
+ # §111 — compression/projection may never strip these from a capsule.
19
+ ESSENTIAL_FIELDS = ("facts", "findings", "open_questions")
20
+
21
+
22
+ @dataclass
23
+ class ContextCapsule:
24
+ schema: str = SCHEMA
25
+ capsule_id: str = ""
26
+ summary: str = ""
27
+ task: str = ""
28
+ scope: str = "repository"
29
+ facts: list[Any] = field(default_factory=list)
30
+ findings: list[Any] = field(default_factory=list)
31
+ rules: list[str] = field(default_factory=list)
32
+ knowledge_refs: list[str] = field(default_factory=list)
33
+ graph_refs: list[str] = field(default_factory=list)
34
+ artifact_refs: list[str] = field(default_factory=list)
35
+ open_questions: list[str] = field(default_factory=list)
36
+ budget: dict[str, Any] = field(default_factory=dict)
37
+ expandable: dict[str, str] = field(default_factory=dict)
38
+ # section name → context:// uri with full detail (§62)
39
+
40
+ def __post_init__(self) -> None:
41
+ if self.scope not in SCOPES:
42
+ raise ValueError(f"scope {self.scope!r} not in {SCOPES}")
43
+
44
+ def to_dict(self) -> dict[str, Any]:
45
+ return asdict(self)
46
+
47
+ def serialize(self) -> bytes:
48
+ return json.dumps(self.to_dict(), sort_keys=True,
49
+ separators=(",", ":"), default=str).encode()
50
+
51
+ @property
52
+ def byte_size(self) -> int:
53
+ return len(self.serialize())
54
+
55
+ def content_hash(self) -> str:
56
+ import hashlib
57
+ return hashlib.sha256(self.serialize()).hexdigest()
58
+
59
+ def ref(self) -> str:
60
+ return f"context://sha256/{self.content_hash()}"
@@ -0,0 +1,227 @@
1
+ """ContextGateway (§56–75) — the single context layer.
2
+
3
+ Pipeline (§58): task → scope → search → graph expansion → evidence
4
+ selection → dedup → budget → pack → redaction → ContextRef.
5
+
6
+ Every model/agent entry goes through `build()`; the capsule it returns is
7
+ the minimal sufficient context, not the maximal available one (§297).
8
+ Essential evidence that cannot fit the budget is a refusal
9
+ (PF-CONTEXT-ESSENTIAL) — never silently truncated (§235 eval).
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ import time
16
+ from dataclasses import dataclass, field
17
+ from pathlib import Path
18
+ from typing import Any
19
+
20
+ from platformforge.context.capsule import ContextCapsule
21
+ from platformforge.context.refs import ContextRef
22
+ from platformforge.context.sufficiency import ContextSufficiencyResult, Sufficiency, assess
23
+ from platformforge.economy.cache import CacheStore
24
+ from platformforge.economy.ledger import EconomyLedger, ToolUsageEntry
25
+
26
+ REFUSAL_ESSENTIAL = "PF-CONTEXT-ESSENTIAL"
27
+
28
+ # §70–73 — which capsule sections each role receives.
29
+ ROLE_SECTIONS: dict[str, tuple[str, ...]] = {
30
+ "specialist": ("summary", "task", "scope", "facts", "findings",
31
+ "rules", "knowledge_refs", "graph_refs",
32
+ "artifact_refs", "open_questions"),
33
+ "reviewer": ("summary", "task", "scope", "findings", "rules",
34
+ "open_questions"),
35
+ "verifier": ("summary", "task", "findings", "rules",
36
+ "open_questions"), # §71: criteria+evidence+outputs
37
+ "critic": ("summary", "task", "open_questions"), # §72 assumptions
38
+ "referee": ("summary", "task", "open_questions"),
39
+ "planner": ("summary", "task", "scope", "open_questions",
40
+ "knowledge_refs"),
41
+ }
42
+
43
+
44
+ @dataclass
45
+ class ContextRequest:
46
+ task: str
47
+ scope: str = "repository"
48
+ budget_bytes: int | None = None # soft context budget
49
+ essential_bytes: int | None = None # §235 — must-fit floor
50
+ required_sections: list[str] = field(default_factory=list)
51
+ role: str = "specialist"
52
+ query: str = ""
53
+ facts: list[dict[str, Any]] = field(default_factory=list)
54
+ findings: list[dict[str, Any]] = field(default_factory=list)
55
+ rules: list[str] = field(default_factory=list)
56
+ knowledge_refs: list[str] = field(default_factory=list)
57
+ graph_refs: list[str] = field(default_factory=list)
58
+ artifact_refs: list[str] = field(default_factory=list)
59
+ open_questions: list[str] = field(default_factory=list)
60
+ deps: dict[str, str] = field(default_factory=dict)
61
+
62
+
63
+ class ContextGateway:
64
+ def __init__(self, root: str | Path):
65
+ self.root = Path(root)
66
+ self.cache = CacheStore(root)
67
+ self.ledger = EconomyLedger(root)
68
+ self._store = self.root / ".platformforge" / "context-store"
69
+ self._store.mkdir(parents=True, exist_ok=True)
70
+
71
+ # -- build (pipeline §58) ------------------------------------------
72
+ def build(self, req: ContextRequest) -> dict[str, Any]:
73
+ t0 = time.monotonic()
74
+ # 1–2. task + scope are inputs; 3–4. search/graph expansion is the
75
+ # caller's job (facts/findings arrive already extracted — the
76
+ # gateway assembles, it does not analyze).
77
+ # 5. evidence selection: essential sections first, dedup (§6).
78
+ facts = _dedup(req.facts, key=lambda f: f.get("fact_id",
79
+ json.dumps(f,
80
+ sort_keys=True,
81
+ default=str)))
82
+ findings = _dedup(req.findings, key=lambda f: f.get(
83
+ "finding_id", f.get("rule_id", json.dumps(f, sort_keys=True,
84
+ default=str))))
85
+
86
+ capsule = ContextCapsule(
87
+ summary=f"{req.task} ({req.scope})",
88
+ task=req.task, scope=req.scope,
89
+ facts=facts, findings=findings, rules=req.rules,
90
+ knowledge_refs=req.knowledge_refs, graph_refs=req.graph_refs,
91
+ artifact_refs=req.artifact_refs,
92
+ open_questions=req.open_questions,
93
+ budget={"budget_bytes": req.budget_bytes,
94
+ "essential_bytes": req.essential_bytes})
95
+
96
+ # 7. budget — essential evidence must fit or the capsule refuses
97
+ size = capsule.byte_size
98
+ if req.essential_bytes is not None and \
99
+ req.budget_bytes is not None and \
100
+ req.essential_bytes > req.budget_bytes:
101
+ self.ledger.record(ToolUsageEntry(
102
+ tool="context.build", output_bytes=size,
103
+ duration_ms=(time.monotonic() - t0) * 1000,
104
+ task=req.task))
105
+ return {"refusal": REFUSAL_ESSENTIAL,
106
+ "reason": f"essential {req.essential_bytes}B > "
107
+ f"budget {req.budget_bytes}B — narrow scope "
108
+ "or raise budget; never truncate evidence",
109
+ "sufficiency": ContextSufficiencyResult(
110
+ Sufficiency.INSUFFICIENT,
111
+ ["essential-evidence"],
112
+ "budget smaller than essential floor").to_dict()}
113
+
114
+ # 8. redaction — secrets never persist into the store (§224)
115
+ self._persist(capsule)
116
+
117
+ # 9. ContextRef + sufficiency + cache receipt
118
+ ref = capsule.ref()
119
+ suff = assess(required=req.required_sections,
120
+ present=[s for s in ("facts", "findings", "rules",
121
+ "knowledge_refs",
122
+ "graph_refs")
123
+ if getattr(capsule, s)])
124
+ self.ledger.record(ToolUsageEntry(
125
+ tool="context.build", output_bytes=size,
126
+ duration_ms=(time.monotonic() - t0) * 1000,
127
+ task=req.task))
128
+ return {"capsule": capsule.to_dict(), "ref": ref,
129
+ "bytes": size, "sufficiency": suff.to_dict(),
130
+ "expandable": capsule.expandable}
131
+
132
+ # -- lazy refs (§63–64) ---------------------------------------------
133
+ def _persist(self, capsule: ContextCapsule) -> None:
134
+ # §224 — redact before persistence (the capsule holds refs; the
135
+ # payload sections may still carry sensitive text from callers)
136
+ blob = _redact(capsule.to_dict())
137
+ (self._store / f"{capsule.content_hash()}.json").write_text(
138
+ json.dumps(blob, sort_keys=True, default=str))
139
+
140
+ def inspect(self, ref: str) -> dict[str, Any]:
141
+ """Resolve a `context://sha256/<hash>` ref to its stored capsule."""
142
+ digest = ContextRef.parse(ref).digest
143
+ p = self._store / f"{digest}.json"
144
+ if not p.exists():
145
+ return {"refusal": "PF-CONTEXT-REF-UNKNOWN",
146
+ "reason": f"no stored capsule for {ref}",
147
+ "unlock": "rebuild the context that minted the ref"}
148
+ return json.loads(p.read_text())
149
+
150
+ def expand(self, ref: str, section: str = "") -> dict[str, Any]:
151
+ """§63–64 — lazy expansion: return only the requested section
152
+ (or the full capsule when section is empty)."""
153
+ cap = self.inspect(ref)
154
+ if "refusal" in cap:
155
+ return cap
156
+ if section:
157
+ if section not in cap:
158
+ return {"refusal": "PF-CONTEXT-SECTION-UNKNOWN",
159
+ "reason": f"capsule has no section {section!r}",
160
+ "sections": sorted(k for k in cap
161
+ if not k.startswith("_"))}
162
+ return {"ref": ref, "section": section,
163
+ "content": cap[section]}
164
+ return {"ref": ref, "capsule": cap}
165
+
166
+ def delta(self, base_ref: str, capsule: ContextCapsule
167
+ ) -> dict[str, Any]:
168
+ """§74 — baseline + delta: emit only the sections that changed vs
169
+ the stored baseline capsule. Unchanged sections ship as the ref."""
170
+ base = self.inspect(base_ref)
171
+ if "refusal" in base:
172
+ return base
173
+ delta: dict[str, Any] = {}
174
+ reused: list[str] = []
175
+ for k, v in capsule.to_dict().items():
176
+ if k in ("schema",):
177
+ continue
178
+ if base.get(k) != v:
179
+ delta[k] = v
180
+ else:
181
+ reused.append(k)
182
+ self._persist(capsule)
183
+ return {"baseline_ref": base_ref, "delta_ref": capsule.ref(),
184
+ "delta": delta, "reused_sections": reused,
185
+ "delta_bytes": len(json.dumps(delta, default=str)),
186
+ "full_bytes": capsule.byte_size}
187
+
188
+ # -- role-specific (§70–73) -----------------------------------------
189
+ def for_role(self, ref: str, role: str) -> dict[str, Any]:
190
+ """Project a stored capsule to the sections a role receives.
191
+ Verifier gets criteria+evidence+outputs — not the transcript."""
192
+ if role not in ROLE_SECTIONS:
193
+ return {"refusal": "PF-CONTEXT-ROLE-UNKNOWN",
194
+ "reason": f"role {role!r} not in "
195
+ f"{sorted(ROLE_SECTIONS)}"}
196
+ cap = self.inspect(ref)
197
+ if "refusal" in cap:
198
+ return cap
199
+ return {"role": role, "ref": ref,
200
+ "context": {s: cap.get(s) for s in ROLE_SECTIONS[role]}}
201
+
202
+
203
+ def _dedup(items: list[dict[str, Any]], key) -> list[dict[str, Any]]:
204
+ seen, out = set(), []
205
+ for it in items:
206
+ k = key(it)
207
+ if k in seen:
208
+ continue
209
+ seen.add(k)
210
+ out.append(it)
211
+ return out
212
+
213
+
214
+ _SECRET_KEYS = ("password", "secret", "token", "api_key", "private_key",
215
+ "credential")
216
+
217
+
218
+ def _redact(obj: Any) -> Any:
219
+ """§224 — boundary redaction before capsule persistence. Values whose
220
+ key looks secret-shaped are replaced, never stored."""
221
+ if isinstance(obj, dict):
222
+ return {k: ("[REDACTED]" if any(s in str(k).lower()
223
+ for s in _SECRET_KEYS)
224
+ else _redact(v)) for k, v in obj.items()}
225
+ if isinstance(obj, list):
226
+ return [_redact(v) for v in obj]
227
+ return obj
@@ -0,0 +1,49 @@
1
+ """Pointer-based context refs (§59, §62) — `context://sha256/<hash>` and
2
+ the typed evidence/fact/graph pointers. Agents receive refs and expand on
3
+ demand (§63–64) instead of receiving duplicated content."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import hashlib
8
+ import json
9
+ from dataclasses import dataclass
10
+ from typing import Any
11
+
12
+ REF_SCHEMES = ("context", "evidence", "fact", "graph", "artifact",
13
+ "knowledge", "finding")
14
+
15
+
16
+ def _hash(payload: Any) -> str:
17
+ body = json.dumps(payload, sort_keys=True, separators=(",", ":"),
18
+ default=str).encode()
19
+ return hashlib.sha256(body).hexdigest()
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class ContextRef:
24
+ """§59 — a content-addressed pointer. `uri` is the wire form."""
25
+ scheme: str # REF_SCHEMES
26
+ digest: str
27
+ hint: str = "" # what the ref points at (for budgets)
28
+
29
+ def __post_init__(self) -> None:
30
+ if self.scheme not in REF_SCHEMES:
31
+ raise ValueError(f"unknown ref scheme {self.scheme!r}")
32
+ if len(self.digest) != 64:
33
+ raise ValueError("ref digest must be sha256 (64 hex)")
34
+
35
+ @property
36
+ def uri(self) -> str:
37
+ return f"{self.scheme}://sha256/{self.digest}"
38
+
39
+ @classmethod
40
+ def of(cls, scheme: str, payload: Any, hint: str = "") -> ContextRef:
41
+ return cls(scheme=scheme, digest=_hash(payload), hint=hint)
42
+
43
+ @classmethod
44
+ def parse(cls, uri: str) -> ContextRef:
45
+ scheme, _, rest = uri.partition("://")
46
+ algo, _, digest = rest.partition("/")
47
+ if algo != "sha256":
48
+ raise ValueError(f"unsupported ref algo {algo!r}")
49
+ return cls(scheme=scheme, digest=digest)
@@ -0,0 +1,50 @@
1
+ """Context sufficiency (§65–67) and quality (§69)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict, dataclass, field
6
+ from typing import Any
7
+
8
+
9
+ class Sufficiency:
10
+ SUFFICIENT = "sufficient"
11
+ PARTIAL = "partial"
12
+ INSUFFICIENT = "insufficient" # §67 — never answer with confidence
13
+
14
+
15
+ @dataclass
16
+ class ContextSufficiencyResult:
17
+ state: str # Sufficiency.*
18
+ missing: list[str] = field(default_factory=list)
19
+ reason: str = ""
20
+ coverage: dict[str, float] = field(default_factory=dict)
21
+
22
+ def confident(self) -> bool:
23
+ """§67 — insufficient context forbids confident answers."""
24
+ return self.state == Sufficiency.SUFFICIENT
25
+
26
+ def to_dict(self) -> dict[str, Any]:
27
+ return asdict(self)
28
+
29
+
30
+ def assess(*, required: list[str], present: list[str],
31
+ essential_fit: bool = True) -> ContextSufficiencyResult:
32
+ """Sufficiency = did the needed evidence sections make the pack?
33
+
34
+ `essential_fit=False` means the budget could not hold essential
35
+ evidence → insufficient (never silently truncated, §235)."""
36
+ missing = [r for r in required if r not in present]
37
+ if not essential_fit:
38
+ return ContextSufficiencyResult(
39
+ Sufficiency.INSUFFICIENT, missing or ["essential-evidence"],
40
+ "essential evidence does not fit the context budget",
41
+ coverage={"evidence": (len(present) /
42
+ max(1, len(present) + len(missing)))})
43
+ if missing:
44
+ return ContextSufficiencyResult(
45
+ Sufficiency.PARTIAL, missing,
46
+ f"missing sections: {missing}",
47
+ coverage={"evidence": len(present) /
48
+ max(1, len(present) + len(missing))})
49
+ return ContextSufficiencyResult(
50
+ Sufficiency.SUFFICIENT, coverage={"evidence": 1.0})
@@ -0,0 +1 @@
1
+ """Deterministic core: hashing, artifact store, receipts, redaction, workspace."""