autoops-sre 0.1.3__tar.gz

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 (230) hide show
  1. autoops_sre-0.1.3/.gitignore +38 -0
  2. autoops_sre-0.1.3/LICENSE +21 -0
  3. autoops_sre-0.1.3/PKG-INFO +199 -0
  4. autoops_sre-0.1.3/README.md +169 -0
  5. autoops_sre-0.1.3/pyproject.toml +221 -0
  6. autoops_sre-0.1.3/src/autoops/__init__.py +35 -0
  7. autoops_sre-0.1.3/src/autoops/actions/__init__.py +7 -0
  8. autoops_sre-0.1.3/src/autoops/actions/scale.py +140 -0
  9. autoops_sre-0.1.3/src/autoops/adapters/__init__.py +1 -0
  10. autoops_sre-0.1.3/src/autoops/adapters/aiopslab.py +287 -0
  11. autoops_sre-0.1.3/src/autoops/adapters/anthropic_client.py +82 -0
  12. autoops_sre-0.1.3/src/autoops/adapters/llm.py +110 -0
  13. autoops_sre-0.1.3/src/autoops/adapters/openrouter_client.py +139 -0
  14. autoops_sre-0.1.3/src/autoops/autoctl.py +453 -0
  15. autoops_sre-0.1.3/src/autoops/cli.py +2728 -0
  16. autoops_sre-0.1.3/src/autoops/cli_options.py +42 -0
  17. autoops_sre-0.1.3/src/autoops/config.py +278 -0
  18. autoops_sre-0.1.3/src/autoops/controller/__init__.py +21 -0
  19. autoops_sre-0.1.3/src/autoops/controller/artifacts.py +1597 -0
  20. autoops_sre-0.1.3/src/autoops/controller/budget.py +132 -0
  21. autoops_sre-0.1.3/src/autoops/controller/composition.py +3038 -0
  22. autoops_sre-0.1.3/src/autoops/controller/context_budget.py +207 -0
  23. autoops_sre-0.1.3/src/autoops/controller/engine.py +4000 -0
  24. autoops_sre-0.1.3/src/autoops/controller/judge.py +78 -0
  25. autoops_sre-0.1.3/src/autoops/controller/localization.py +82 -0
  26. autoops_sre-0.1.3/src/autoops/controller/loop.py +131 -0
  27. autoops_sre-0.1.3/src/autoops/controller/mutation.py +312 -0
  28. autoops_sre-0.1.3/src/autoops/controller/owner_join.py +114 -0
  29. autoops_sre-0.1.3/src/autoops/controller/panel.py +141 -0
  30. autoops_sre-0.1.3/src/autoops/controller/rca.py +187 -0
  31. autoops_sre-0.1.3/src/autoops/controller/rca_loop.py +877 -0
  32. autoops_sre-0.1.3/src/autoops/controller/responses.py +200 -0
  33. autoops_sre-0.1.3/src/autoops/controller/senses.py +2110 -0
  34. autoops_sre-0.1.3/src/autoops/controller/stages.py +291 -0
  35. autoops_sre-0.1.3/src/autoops/controller/watch.py +411 -0
  36. autoops_sre-0.1.3/src/autoops/controller/workload_resolution.py +245 -0
  37. autoops_sre-0.1.3/src/autoops/core/__init__.py +3 -0
  38. autoops_sre-0.1.3/src/autoops/core/cell.py +221 -0
  39. autoops_sre-0.1.3/src/autoops/core/events.py +53 -0
  40. autoops_sre-0.1.3/src/autoops/core/evidence_bundle.py +174 -0
  41. autoops_sre-0.1.3/src/autoops/core/guard_error.py +29 -0
  42. autoops_sre-0.1.3/src/autoops/core/messages.py +50 -0
  43. autoops_sre-0.1.3/src/autoops/core/middleware.py +125 -0
  44. autoops_sre-0.1.3/src/autoops/core/registry.py +38 -0
  45. autoops_sre-0.1.3/src/autoops/delivery.py +307 -0
  46. autoops_sre-0.1.3/src/autoops/eval/__init__.py +14 -0
  47. autoops_sre-0.1.3/src/autoops/eval/admission.py +177 -0
  48. autoops_sre-0.1.3/src/autoops/eval/agent.py +657 -0
  49. autoops_sre-0.1.3/src/autoops/eval/axis_gap.py +400 -0
  50. autoops_sre-0.1.3/src/autoops/eval/briefing.py +174 -0
  51. autoops_sre-0.1.3/src/autoops/eval/campaign_declaration.py +324 -0
  52. autoops_sre-0.1.3/src/autoops/eval/campaign_report.py +1508 -0
  53. autoops_sre-0.1.3/src/autoops/eval/decision.py +141 -0
  54. autoops_sre-0.1.3/src/autoops/eval/fault_verifier.py +647 -0
  55. autoops_sre-0.1.3/src/autoops/eval/live_orchestrator.py +1093 -0
  56. autoops_sre-0.1.3/src/autoops/eval/measure.py +207 -0
  57. autoops_sre-0.1.3/src/autoops/eval/miss_record.py +619 -0
  58. autoops_sre-0.1.3/src/autoops/eval/miss_scenario.py +439 -0
  59. autoops_sre-0.1.3/src/autoops/eval/offline_door.py +565 -0
  60. autoops_sre-0.1.3/src/autoops/eval/offline_orchestrator.py +535 -0
  61. autoops_sre-0.1.3/src/autoops/eval/offline_series.py +136 -0
  62. autoops_sre-0.1.3/src/autoops/eval/panel.py +91 -0
  63. autoops_sre-0.1.3/src/autoops/eval/process_metrics.py +211 -0
  64. autoops_sre-0.1.3/src/autoops/eval/reachability_probe.py +127 -0
  65. autoops_sre-0.1.3/src/autoops/eval/runner.py +1269 -0
  66. autoops_sre-0.1.3/src/autoops/eval/scoring.py +378 -0
  67. autoops_sre-0.1.3/src/autoops/eval/strata.py +385 -0
  68. autoops_sre-0.1.3/src/autoops/eval/trajectory.py +207 -0
  69. autoops_sre-0.1.3/src/autoops/eval/verify_channels.py +93 -0
  70. autoops_sre-0.1.3/src/autoops/eval/verify_event_join.py +309 -0
  71. autoops_sre-0.1.3/src/autoops/memory/__init__.py +6 -0
  72. autoops_sre-0.1.3/src/autoops/memory/estate_store.py +346 -0
  73. autoops_sre-0.1.3/src/autoops/memory/versioning.py +163 -0
  74. autoops_sre-0.1.3/src/autoops/observability/__init__.py +19 -0
  75. autoops_sre-0.1.3/src/autoops/observability/abstraction.py +128 -0
  76. autoops_sre-0.1.3/src/autoops/observability/alert_rules.py +762 -0
  77. autoops_sre-0.1.3/src/autoops/observability/answer_bound.py +179 -0
  78. autoops_sre-0.1.3/src/autoops/observability/autoscaler_reach.py +249 -0
  79. autoops_sre-0.1.3/src/autoops/observability/aws_credentials.py +160 -0
  80. autoops_sre-0.1.3/src/autoops/observability/aws_sigv4.py +166 -0
  81. autoops_sre-0.1.3/src/autoops/observability/backend_fingerprint.py +105 -0
  82. autoops_sre-0.1.3/src/autoops/observability/baselines.py +304 -0
  83. autoops_sre-0.1.3/src/autoops/observability/budget_dwell.py +355 -0
  84. autoops_sre-0.1.3/src/autoops/observability/capability_probes.py +473 -0
  85. autoops_sre-0.1.3/src/autoops/observability/carrier_relation.py +221 -0
  86. autoops_sre-0.1.3/src/autoops/observability/causal_localizer.py +452 -0
  87. autoops_sre-0.1.3/src/autoops/observability/causal_rca.py +4644 -0
  88. autoops_sre-0.1.3/src/autoops/observability/channels.py +117 -0
  89. autoops_sre-0.1.3/src/autoops/observability/citation_grounding.py +258 -0
  90. autoops_sre-0.1.3/src/autoops/observability/claim_binding.py +282 -0
  91. autoops_sre-0.1.3/src/autoops/observability/cloudwatch.py +638 -0
  92. autoops_sre-0.1.3/src/autoops/observability/concurrency_wedge.py +275 -0
  93. autoops_sre-0.1.3/src/autoops/observability/connectors.py +144 -0
  94. autoops_sre-0.1.3/src/autoops/observability/control_plane_oracle.py +204 -0
  95. autoops_sre-0.1.3/src/autoops/observability/control_plane_posture.py +91 -0
  96. autoops_sre-0.1.3/src/autoops/observability/control_plane_rbac.py +407 -0
  97. autoops_sre-0.1.3/src/autoops/observability/control_plane_reader.py +3263 -0
  98. autoops_sre-0.1.3/src/autoops/observability/daemon_scheduling.py +158 -0
  99. autoops_sre-0.1.3/src/autoops/observability/datasources.py +129 -0
  100. autoops_sre-0.1.3/src/autoops/observability/declared_topology.py +2256 -0
  101. autoops_sre-0.1.3/src/autoops/observability/dependencies.py +1122 -0
  102. autoops_sre-0.1.3/src/autoops/observability/deploy_digest.py +110 -0
  103. autoops_sre-0.1.3/src/autoops/observability/discovery.py +894 -0
  104. autoops_sre-0.1.3/src/autoops/observability/disruption_budget.py +589 -0
  105. autoops_sre-0.1.3/src/autoops/observability/entities.py +213 -0
  106. autoops_sre-0.1.3/src/autoops/observability/entity_existence.py +249 -0
  107. autoops_sre-0.1.3/src/autoops/observability/estate_map.py +571 -0
  108. autoops_sre-0.1.3/src/autoops/observability/estate_model.py +723 -0
  109. autoops_sre-0.1.3/src/autoops/observability/event_digest.py +279 -0
  110. autoops_sre-0.1.3/src/autoops/observability/event_identity.py +62 -0
  111. autoops_sre-0.1.3/src/autoops/observability/event_provenance.py +169 -0
  112. autoops_sre-0.1.3/src/autoops/observability/evidence_gate.py +329 -0
  113. autoops_sre-0.1.3/src/autoops/observability/evidence_graph.py +457 -0
  114. autoops_sre-0.1.3/src/autoops/observability/gateway_reach.py +346 -0
  115. autoops_sre-0.1.3/src/autoops/observability/golden_signals.py +59 -0
  116. autoops_sre-0.1.3/src/autoops/observability/golden_sweep.py +240 -0
  117. autoops_sre-0.1.3/src/autoops/observability/governing_service.py +219 -0
  118. autoops_sre-0.1.3/src/autoops/observability/governor.py +1012 -0
  119. autoops_sre-0.1.3/src/autoops/observability/graph_reasoning.py +552 -0
  120. autoops_sre-0.1.3/src/autoops/observability/httpget.py +80 -0
  121. autoops_sre-0.1.3/src/autoops/observability/ingress_reach.py +387 -0
  122. autoops_sre-0.1.3/src/autoops/observability/investigation.py +247 -0
  123. autoops_sre-0.1.3/src/autoops/observability/jaeger.py +525 -0
  124. autoops_sre-0.1.3/src/autoops/observability/job_failure.py +272 -0
  125. autoops_sre-0.1.3/src/autoops/observability/keyword_denylists.py +116 -0
  126. autoops_sre-0.1.3/src/autoops/observability/kind_inventory.py +250 -0
  127. autoops_sre-0.1.3/src/autoops/observability/label_sets.py +44 -0
  128. autoops_sre-0.1.3/src/autoops/observability/live_candidate_logs.py +150 -0
  129. autoops_sre-0.1.3/src/autoops/observability/live_deployments.py +52 -0
  130. autoops_sre-0.1.3/src/autoops/observability/live_logs.py +70 -0
  131. autoops_sre-0.1.3/src/autoops/observability/live_pods.py +100 -0
  132. autoops_sre-0.1.3/src/autoops/observability/live_service_ports.py +64 -0
  133. autoops_sre-0.1.3/src/autoops/observability/live_service_red.py +112 -0
  134. autoops_sre-0.1.3/src/autoops/observability/log_digest.py +200 -0
  135. autoops_sre-0.1.3/src/autoops/observability/loki.py +664 -0
  136. autoops_sre-0.1.3/src/autoops/observability/metrics_digest.py +137 -0
  137. autoops_sre-0.1.3/src/autoops/observability/network_policy_reach.py +374 -0
  138. autoops_sre-0.1.3/src/autoops/observability/node_condition.py +324 -0
  139. autoops_sre-0.1.3/src/autoops/observability/node_placement.py +163 -0
  140. autoops_sre-0.1.3/src/autoops/observability/observation_composition.py +260 -0
  141. autoops_sre-0.1.3/src/autoops/observability/pipeline_health.py +281 -0
  142. autoops_sre-0.1.3/src/autoops/observability/placement_refusal.py +220 -0
  143. autoops_sre-0.1.3/src/autoops/observability/pod_binding_digest.py +465 -0
  144. autoops_sre-0.1.3/src/autoops/observability/pod_ownership.py +111 -0
  145. autoops_sre-0.1.3/src/autoops/observability/pod_placement.py +265 -0
  146. autoops_sre-0.1.3/src/autoops/observability/pod_status.py +754 -0
  147. autoops_sre-0.1.3/src/autoops/observability/probe_outcome.py +47 -0
  148. autoops_sre-0.1.3/src/autoops/observability/prometheus.py +950 -0
  149. autoops_sre-0.1.3/src/autoops/observability/promql_series.py +268 -0
  150. autoops_sre-0.1.3/src/autoops/observability/provenance_digest.py +173 -0
  151. autoops_sre-0.1.3/src/autoops/observability/reachability.py +101 -0
  152. autoops_sre-0.1.3/src/autoops/observability/read_audit.py +331 -0
  153. autoops_sre-0.1.3/src/autoops/observability/replica_failure.py +305 -0
  154. autoops_sre-0.1.3/src/autoops/observability/request_path_digest.py +206 -0
  155. autoops_sre-0.1.3/src/autoops/observability/resource_key.py +633 -0
  156. autoops_sre-0.1.3/src/autoops/observability/route_reach.py +579 -0
  157. autoops_sre-0.1.3/src/autoops/observability/schedule_reach.py +366 -0
  158. autoops_sre-0.1.3/src/autoops/observability/scheduling_failure.py +443 -0
  159. autoops_sre-0.1.3/src/autoops/observability/scope_selection.py +187 -0
  160. autoops_sre-0.1.3/src/autoops/observability/sense_evidence.py +1309 -0
  161. autoops_sre-0.1.3/src/autoops/observability/service_port_digest.py +366 -0
  162. autoops_sre-0.1.3/src/autoops/observability/service_proxy.py +87 -0
  163. autoops_sre-0.1.3/src/autoops/observability/service_red.py +133 -0
  164. autoops_sre-0.1.3/src/autoops/observability/service_red_digest.py +153 -0
  165. autoops_sre-0.1.3/src/autoops/observability/severity.py +251 -0
  166. autoops_sre-0.1.3/src/autoops/observability/storage_class_inventory.py +285 -0
  167. autoops_sre-0.1.3/src/autoops/observability/structural_finding.py +672 -0
  168. autoops_sre-0.1.3/src/autoops/observability/taint.py +54 -0
  169. autoops_sre-0.1.3/src/autoops/observability/telemetry_families.py +150 -0
  170. autoops_sre-0.1.3/src/autoops/observability/telemetry_stack.py +449 -0
  171. autoops_sre-0.1.3/src/autoops/observability/tenancy.py +609 -0
  172. autoops_sre-0.1.3/src/autoops/observability/timestamps.py +28 -0
  173. autoops_sre-0.1.3/src/autoops/observability/topology.py +1084 -0
  174. autoops_sre-0.1.3/src/autoops/observability/traces_digest.py +228 -0
  175. autoops_sre-0.1.3/src/autoops/observability/verifier.py +109 -0
  176. autoops_sre-0.1.3/src/autoops/observability/volume_reclaim.py +202 -0
  177. autoops_sre-0.1.3/src/autoops/observability/workload_history.py +359 -0
  178. autoops_sre-0.1.3/src/autoops/onboarding/__init__.py +11 -0
  179. autoops_sre-0.1.3/src/autoops/onboarding/allowlist.py +309 -0
  180. autoops_sre-0.1.3/src/autoops/onboarding/commands.py +254 -0
  181. autoops_sre-0.1.3/src/autoops/onboarding/endpoints.py +183 -0
  182. autoops_sre-0.1.3/src/autoops/onboarding/identity.py +80 -0
  183. autoops_sre-0.1.3/src/autoops/onboarding/runconfig.py +43 -0
  184. autoops_sre-0.1.3/src/autoops/policy/__init__.py +12 -0
  185. autoops_sre-0.1.3/src/autoops/policy/cedar/ops.cedar +179 -0
  186. autoops_sre-0.1.3/src/autoops/policy/cedar/ops.cedarschema +109 -0
  187. autoops_sre-0.1.3/src/autoops/policy/cedar_diagnostics.py +57 -0
  188. autoops_sre-0.1.3/src/autoops/policy/cedar_guard.py +202 -0
  189. autoops_sre-0.1.3/src/autoops/policy/cloudwatch_read_guard.py +74 -0
  190. autoops_sre-0.1.3/src/autoops/policy/control_plane_read_guard.py +125 -0
  191. autoops_sre-0.1.3/src/autoops/policy/destructive.py +128 -0
  192. autoops_sre-0.1.3/src/autoops/policy/envelope.py +50 -0
  193. autoops_sre-0.1.3/src/autoops/policy/mutation_approval.py +315 -0
  194. autoops_sre-0.1.3/src/autoops/policy/oracle_guard.py +236 -0
  195. autoops_sre-0.1.3/src/autoops/policy/reachability_guard.py +108 -0
  196. autoops_sre-0.1.3/src/autoops/policy/readonly_guard.py +58 -0
  197. autoops_sre-0.1.3/src/autoops/policy/scale_guard.py +207 -0
  198. autoops_sre-0.1.3/src/autoops/policy/shell_gate.py +955 -0
  199. autoops_sre-0.1.3/src/autoops/sandbox/__init__.py +55 -0
  200. autoops_sre-0.1.3/src/autoops/sandbox/cluster_guard.py +170 -0
  201. autoops_sre-0.1.3/src/autoops/sandbox/crossing.py +1122 -0
  202. autoops_sre-0.1.3/src/autoops/sandbox/egress.py +541 -0
  203. autoops_sre-0.1.3/src/autoops/sandbox/executor.py +318 -0
  204. autoops_sre-0.1.3/src/autoops/sandbox/identity.py +499 -0
  205. autoops_sre-0.1.3/src/autoops/sandbox/k8s_read_request.py +578 -0
  206. autoops_sre-0.1.3/src/autoops/sandbox/k8s_read_transport.py +130 -0
  207. autoops_sre-0.1.3/src/autoops/sandbox/pinned_http.py +216 -0
  208. autoops_sre-0.1.3/src/autoops/sandbox/preflight.py +89 -0
  209. autoops_sre-0.1.3/src/autoops/sandbox/probe_executor.py +153 -0
  210. autoops_sre-0.1.3/src/autoops/sandbox/probe_target.py +113 -0
  211. autoops_sre-0.1.3/src/autoops/sandbox/scale_executor.py +178 -0
  212. autoops_sre-0.1.3/src/autoops/sandbox/token_minter.py +298 -0
  213. autoops_sre-0.1.3/src/autoops/selftest.py +71 -0
  214. autoops_sre-0.1.3/src/autoops/serve/__init__.py +23 -0
  215. autoops_sre-0.1.3/src/autoops/serve/api.py +386 -0
  216. autoops_sre-0.1.3/src/autoops/serve/auth.py +149 -0
  217. autoops_sre-0.1.3/src/autoops/serve/authority.py +153 -0
  218. autoops_sre-0.1.3/src/autoops/serve/commands.py +285 -0
  219. autoops_sre-0.1.3/src/autoops/serve/mcp_server.py +109 -0
  220. autoops_sre-0.1.3/src/autoops/serve/models.py +306 -0
  221. autoops_sre-0.1.3/src/autoops/serve/runs.py +265 -0
  222. autoops_sre-0.1.3/src/autoops/serve/stream.py +75 -0
  223. autoops_sre-0.1.3/src/autoops/serve/tools.py +247 -0
  224. autoops_sre-0.1.3/src/autoops/telemetry/__init__.py +3 -0
  225. autoops_sre-0.1.3/src/autoops/telemetry/comparability.py +231 -0
  226. autoops_sre-0.1.3/src/autoops/telemetry/ledger.py +306 -0
  227. autoops_sre-0.1.3/src/autoops/telemetry/manifest.py +733 -0
  228. autoops_sre-0.1.3/src/autoops/telemetry/otel.py +25 -0
  229. autoops_sre-0.1.3/src/autoops/telemetry/stratum.py +40 -0
  230. autoops_sre-0.1.3/uv.lock +2332 -0
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # uv / venv
10
+ .venv/
11
+ .python-version
12
+
13
+ # Tooling caches
14
+ .ruff_cache/
15
+ .mypy_cache/
16
+ .pytest_cache/
17
+ .coverage
18
+ .coverage.*
19
+ htmlcov/
20
+ coverage.xml
21
+
22
+ # Artifacts, evidence, traces and SBOM are committed as a backup
23
+ # (large >3MB dumps go through Git LFS, see .gitattributes).
24
+ # Only transient root-level logs stay ignored; logs inside the dirs are kept.
25
+ /*.log
26
+
27
+ # Editor / OS
28
+ .vscode/
29
+ .idea/
30
+ .DS_Store
31
+
32
+ # Secrets (never commit credentials)
33
+ .env
34
+ .env.*
35
+ !.env.example
36
+
37
+ # Claude Code personal project settings
38
+ .claude/settings.local.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evangelos Pappas
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,199 @@
1
+ Metadata-Version: 2.4
2
+ Name: autoops-sre
3
+ Version: 0.1.3
4
+ Summary: An agentic ops harness for AIOps — SRE, cloud, Kubernetes, observability, and security automation.
5
+ Project-URL: Repository, https://github.com/epappas/autoops
6
+ Author-email: Evangelos Pappas <epappas@evalonlabs.com>
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: agent,aiops,harness,kubernetes,observability,sre
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: System :: Systems Administration
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: anthropic>=0.121.0
17
+ Requires-Dist: cedarpy<5,>=4.8.7
18
+ Requires-Dist: cryptography>=50
19
+ Requires-Dist: mcp>=2.1.1
20
+ Requires-Dist: openai>=2.53.0
21
+ Requires-Dist: opentelemetry-api>=1.43.0
22
+ Requires-Dist: opentelemetry-sdk>=1.44.0
23
+ Requires-Dist: pydantic>=2.7
24
+ Requires-Dist: pyyaml>=6.0
25
+ Requires-Dist: starlette>=1.6.0
26
+ Requires-Dist: typer>=0.27.1
27
+ Requires-Dist: uvicorn>=0.52.4
28
+ Requires-Dist: websockets>=17.1
29
+ Description-Content-Type: text/markdown
30
+
31
+ # autoOps
32
+
33
+ An autonomous SRE agent. Point it at a Kubernetes namespace and it models the estate,
34
+ names the service that is broken, shows the evidence it used, and re-reads the cluster to
35
+ check its own answer — or hands a human a briefing saying why it could not.
36
+
37
+ **Prime objective:** an agent that operates real estates unaided through one core runtime
38
+ loop. The measure of done is **transfer** — the loop working on an estate it was not built
39
+ for (ADR-0038 D6) — never a benchmark score.
40
+
41
+ > **What works today.** The nine-stage loop is built and has run against real clusters
42
+ > read-only. Perception is deterministic and comes first: 35 senses read the cluster's own
43
+ > declared state and nominate what looks broken, while metrics, traces and logs supply the
44
+ > evidence that either confirms a candidate or refuses it. A sense never votes on its own
45
+ > nomination. Diagnosis then reaches a confident answer or escalates, and says which.
46
+ >
47
+ > **What it deliberately will not do.** It does not act. Stage 8 emits an incident note or
48
+ > a remediation proposal, never a change. The one typed mutation verb that exists,
49
+ > `k8s_scale_workload`, has an executor that raises unconditionally and is reachable from no
50
+ > live path — the boundary is code, not policy. It stays locked until the reasoning is
51
+ > trustworthy enough to earn it.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ uv tool install autoops-sre # or: pip install autoops-sre
57
+ docker pull ghcr.io/epappas/autoops:latest # entrypoint is `autoops`
58
+ ```
59
+
60
+ The distribution is **`autoops-sre`**; the command it installs is **`autoops`**. The bare name
61
+ `autoops` on PyPI belongs to an unrelated project — do not install it.
62
+
63
+ ```bash
64
+ autoops version && autoops --help
65
+ ```
66
+
67
+ Three paths — a Python tool, a container image, or a source checkout — with the exact commands,
68
+ what the image does and does not contain, and how to verify its provenance:
69
+ **[`docs/operations/install.md`](docs/operations/install.md)**.
70
+
71
+ ## The Loop
72
+
73
+ ```
74
+ recall -> understand -> [detect -> map topology -> map services -> localize] -> rca -> mitigate -> remember
75
+ ```
76
+
77
+ The bracket is the investigation. `rca` matches over everything inside it plus what was
78
+ recalled; `remember` closes the loop so the next run starts from a model rather than from
79
+ nothing. Perception is deterministic and precedes action — the estate model generates the
80
+ agent's moves, not the other way round (ADR-0038, ADR-0039).
81
+
82
+ ## Try It Without A Cluster
83
+
84
+ A real model drives the real agent stack over committed recordings — no cluster, and no
85
+ credentials beyond an LLM key. The run is graded against the fixture's known answer, so you
86
+ see both what it concluded and whether it was right.
87
+
88
+ ```bash
89
+ uv sync
90
+ export OPENROUTER_API_KEY=... # or put both in .env (never committed)
91
+ export OPENROUTER_MODEL=anthropic/claude-opus-4.8
92
+
93
+ uv run autoops offline --fixtures tests/fixtures/observability --results-dir ./out
94
+ ```
95
+
96
+ It writes the trajectory, `results.tsv` and — only if it was wrong — a miss record, all under
97
+ `./out`. Four problems are available: `--problem localization` (default), `detection`, `rca`,
98
+ `cascade-localization`.
99
+
100
+ ## Running It Against A Cluster
101
+
102
+ Read **[`docs/operations/operator-runbook.md`](docs/operations/operator-runbook.md)** — the
103
+ operator's document: prerequisites, exit codes and what to do about each, the exact shape of
104
+ a confident answer and of an escalation, troubleshooting, and the safety rails.
105
+
106
+ The short version, on a loopback (kind/k3s) cluster:
107
+
108
+ ```bash
109
+ uv run autoops diagnose --namespace <ns> --results-dir ./out
110
+ ```
111
+
112
+ It runs a three-vote panel and decides fail-closed: **exit 0** is a confident answer,
113
+ **exit 3** is an escalation briefing for a human, **exit 4** means every sense came back
114
+ without a read and no answer above it can be trusted.
115
+
116
+ Once an estate is written into a config file — `./autoops.yaml`, or `--config <path>`, or
117
+ `$AUTOOPS_CONFIG` — a routine diagnosis is `autoops diagnose --namespace <ns>` and nothing
118
+ else. Every flag overrides the config, the config overrides the environment, and with no
119
+ config file every command resolves exactly as it did before (ADR-0206).
120
+
121
+ `autoops watch` is the continuous mode — scheduled panels with a cadence floor, a rolling
122
+ token budget, and escalations delivered to a directory or a webhook.
123
+
124
+ Any cluster that is not loopback is refused before a single read until it is named in an
125
+ allowlist the owner has signed out of band. That refusal is the authorization boundary and
126
+ it lives in the loader, not in the model.
127
+
128
+ ## The Command Surface
129
+
130
+ Seven verbs, plus `version` (ADR-0206, ADR-0208):
131
+
132
+ | Verb | What it is for |
133
+ |---|---|
134
+ | `init` | Onboard an estate and check the setup: `init verify` (the crossing kill-switch), `init validate`, `init print-config`, `init selftest` |
135
+ | `diagnose` | Diagnose a live namespace read-only — the product path |
136
+ | `watch` | Continuous mode: scheduled panels, cadence floor, budget, delivered escalations |
137
+ | `offline` | One committed fixture problem, no cluster, recorded telemetry only |
138
+ | `export` | `export evidence` bundles a finished run for a ticket; `export misses` turns recorded misses into replayable scenarios |
139
+ | `serve` | Serve over the network: `serve http` (REST + websockets + MCP), `serve mcp-stdio` (MCP for a local agent) |
140
+ | `eval` | Evaluation and campaign instrumentation — `declare-campaign`, `campaign-report`, `miss-replay`, `axis-gap` |
141
+ | `version` | Print the version |
142
+
143
+ Every pre-ADR-0206 command name still dispatches, hidden and with a deprecation notice
144
+ naming its replacement, because committed evidence manifests and estate crossing scripts
145
+ record those invocations verbatim.
146
+
147
+ ## Safety
148
+
149
+ autoOps is built to run against real infrastructure, so the constraints come first:
150
+
151
+ - **The model is never the authorization boundary.** Every tool call passes a
152
+ deny-by-default policy gate before execution, and gets a structured reason it can replan
153
+ on rather than a bare refusal.
154
+ - **Read-only by construction on the shipped path.** The Cedar read-only profile plus a
155
+ destructive re-check; a diagnosis cannot mutate.
156
+ - **Approvals are short-lived, target-scoped and re-checked** before every target contact —
157
+ deleting an allowlist entry aborts a run already in flight.
158
+ - **Secrets never enter the model context or the logs.**
159
+
160
+ ## Documentation
161
+
162
+ **[`docs/`](docs/index.md)** is the map — every document, what it answers, and who it is for.
163
+ The four an operator needs:
164
+
165
+ | Task | Document |
166
+ |---|---|
167
+ | Install it | [`operations/install.md`](docs/operations/install.md) |
168
+ | Point it at a cluster | [`operations/onboarding-a-cluster.md`](docs/operations/onboarding-a-cluster.md) |
169
+ | Run a diagnosis and read the answer | [`operations/operator-runbook.md`](docs/operations/operator-runbook.md) |
170
+ | Run it unattended, or over the network | [`operations/continuous.md`](docs/operations/continuous.md) · [`operations/api.md`](docs/operations/api.md) |
171
+
172
+ ## Design
173
+
174
+ - Decisions: [`docs/architecture/adr/`](docs/architecture/adr/) — ADR-0038 and ADR-0039
175
+ define the loop; ADR-0113 records why a sense is a node in the estate graph rather than a
176
+ vote for a culprit.
177
+ - Architecture: [`system-architecture.md`](docs/architecture/system-architecture.md)
178
+ (structure) · [`incident-loop.md`](docs/architecture/incident-loop.md) (behaviour) ·
179
+ [`data-plane.md`](docs/architecture/data-plane.md)
180
+ - Estates: [`deploy/estates/`](deploy/estates/) — eighteen deployable fault estates, each
181
+ with its own contract and offline validators.
182
+ - Research: [`docs/research/`](docs/research/) · beliefs that constrain the design live in
183
+ [`_meta/beliefs.md`](docs/research/_meta/beliefs.md).
184
+
185
+ ## Contributing
186
+
187
+ ```bash
188
+ make help # every target
189
+ make check # the merge gate: lint, imports, types, dead code, tests + coverage, security, estates
190
+ ```
191
+
192
+ See [`CLAUDE.md`](CLAUDE.md) (= `AGENTS.md`) and [`CONTRIBUTING.md`](CONTRIBUTING.md). The
193
+ hard rule: no detection, localization, RCA or mitigation capability is "done" without an
194
+ end-to-end run that closes the telemetry loop — re-reading real cluster state to verify the
195
+ outcome, not the agent's self-report.
196
+
197
+ ## License
198
+
199
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,169 @@
1
+ # autoOps
2
+
3
+ An autonomous SRE agent. Point it at a Kubernetes namespace and it models the estate,
4
+ names the service that is broken, shows the evidence it used, and re-reads the cluster to
5
+ check its own answer — or hands a human a briefing saying why it could not.
6
+
7
+ **Prime objective:** an agent that operates real estates unaided through one core runtime
8
+ loop. The measure of done is **transfer** — the loop working on an estate it was not built
9
+ for (ADR-0038 D6) — never a benchmark score.
10
+
11
+ > **What works today.** The nine-stage loop is built and has run against real clusters
12
+ > read-only. Perception is deterministic and comes first: 35 senses read the cluster's own
13
+ > declared state and nominate what looks broken, while metrics, traces and logs supply the
14
+ > evidence that either confirms a candidate or refuses it. A sense never votes on its own
15
+ > nomination. Diagnosis then reaches a confident answer or escalates, and says which.
16
+ >
17
+ > **What it deliberately will not do.** It does not act. Stage 8 emits an incident note or
18
+ > a remediation proposal, never a change. The one typed mutation verb that exists,
19
+ > `k8s_scale_workload`, has an executor that raises unconditionally and is reachable from no
20
+ > live path — the boundary is code, not policy. It stays locked until the reasoning is
21
+ > trustworthy enough to earn it.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ uv tool install autoops-sre # or: pip install autoops-sre
27
+ docker pull ghcr.io/epappas/autoops:latest # entrypoint is `autoops`
28
+ ```
29
+
30
+ The distribution is **`autoops-sre`**; the command it installs is **`autoops`**. The bare name
31
+ `autoops` on PyPI belongs to an unrelated project — do not install it.
32
+
33
+ ```bash
34
+ autoops version && autoops --help
35
+ ```
36
+
37
+ Three paths — a Python tool, a container image, or a source checkout — with the exact commands,
38
+ what the image does and does not contain, and how to verify its provenance:
39
+ **[`docs/operations/install.md`](docs/operations/install.md)**.
40
+
41
+ ## The Loop
42
+
43
+ ```
44
+ recall -> understand -> [detect -> map topology -> map services -> localize] -> rca -> mitigate -> remember
45
+ ```
46
+
47
+ The bracket is the investigation. `rca` matches over everything inside it plus what was
48
+ recalled; `remember` closes the loop so the next run starts from a model rather than from
49
+ nothing. Perception is deterministic and precedes action — the estate model generates the
50
+ agent's moves, not the other way round (ADR-0038, ADR-0039).
51
+
52
+ ## Try It Without A Cluster
53
+
54
+ A real model drives the real agent stack over committed recordings — no cluster, and no
55
+ credentials beyond an LLM key. The run is graded against the fixture's known answer, so you
56
+ see both what it concluded and whether it was right.
57
+
58
+ ```bash
59
+ uv sync
60
+ export OPENROUTER_API_KEY=... # or put both in .env (never committed)
61
+ export OPENROUTER_MODEL=anthropic/claude-opus-4.8
62
+
63
+ uv run autoops offline --fixtures tests/fixtures/observability --results-dir ./out
64
+ ```
65
+
66
+ It writes the trajectory, `results.tsv` and — only if it was wrong — a miss record, all under
67
+ `./out`. Four problems are available: `--problem localization` (default), `detection`, `rca`,
68
+ `cascade-localization`.
69
+
70
+ ## Running It Against A Cluster
71
+
72
+ Read **[`docs/operations/operator-runbook.md`](docs/operations/operator-runbook.md)** — the
73
+ operator's document: prerequisites, exit codes and what to do about each, the exact shape of
74
+ a confident answer and of an escalation, troubleshooting, and the safety rails.
75
+
76
+ The short version, on a loopback (kind/k3s) cluster:
77
+
78
+ ```bash
79
+ uv run autoops diagnose --namespace <ns> --results-dir ./out
80
+ ```
81
+
82
+ It runs a three-vote panel and decides fail-closed: **exit 0** is a confident answer,
83
+ **exit 3** is an escalation briefing for a human, **exit 4** means every sense came back
84
+ without a read and no answer above it can be trusted.
85
+
86
+ Once an estate is written into a config file — `./autoops.yaml`, or `--config <path>`, or
87
+ `$AUTOOPS_CONFIG` — a routine diagnosis is `autoops diagnose --namespace <ns>` and nothing
88
+ else. Every flag overrides the config, the config overrides the environment, and with no
89
+ config file every command resolves exactly as it did before (ADR-0206).
90
+
91
+ `autoops watch` is the continuous mode — scheduled panels with a cadence floor, a rolling
92
+ token budget, and escalations delivered to a directory or a webhook.
93
+
94
+ Any cluster that is not loopback is refused before a single read until it is named in an
95
+ allowlist the owner has signed out of band. That refusal is the authorization boundary and
96
+ it lives in the loader, not in the model.
97
+
98
+ ## The Command Surface
99
+
100
+ Seven verbs, plus `version` (ADR-0206, ADR-0208):
101
+
102
+ | Verb | What it is for |
103
+ |---|---|
104
+ | `init` | Onboard an estate and check the setup: `init verify` (the crossing kill-switch), `init validate`, `init print-config`, `init selftest` |
105
+ | `diagnose` | Diagnose a live namespace read-only — the product path |
106
+ | `watch` | Continuous mode: scheduled panels, cadence floor, budget, delivered escalations |
107
+ | `offline` | One committed fixture problem, no cluster, recorded telemetry only |
108
+ | `export` | `export evidence` bundles a finished run for a ticket; `export misses` turns recorded misses into replayable scenarios |
109
+ | `serve` | Serve over the network: `serve http` (REST + websockets + MCP), `serve mcp-stdio` (MCP for a local agent) |
110
+ | `eval` | Evaluation and campaign instrumentation — `declare-campaign`, `campaign-report`, `miss-replay`, `axis-gap` |
111
+ | `version` | Print the version |
112
+
113
+ Every pre-ADR-0206 command name still dispatches, hidden and with a deprecation notice
114
+ naming its replacement, because committed evidence manifests and estate crossing scripts
115
+ record those invocations verbatim.
116
+
117
+ ## Safety
118
+
119
+ autoOps is built to run against real infrastructure, so the constraints come first:
120
+
121
+ - **The model is never the authorization boundary.** Every tool call passes a
122
+ deny-by-default policy gate before execution, and gets a structured reason it can replan
123
+ on rather than a bare refusal.
124
+ - **Read-only by construction on the shipped path.** The Cedar read-only profile plus a
125
+ destructive re-check; a diagnosis cannot mutate.
126
+ - **Approvals are short-lived, target-scoped and re-checked** before every target contact —
127
+ deleting an allowlist entry aborts a run already in flight.
128
+ - **Secrets never enter the model context or the logs.**
129
+
130
+ ## Documentation
131
+
132
+ **[`docs/`](docs/index.md)** is the map — every document, what it answers, and who it is for.
133
+ The four an operator needs:
134
+
135
+ | Task | Document |
136
+ |---|---|
137
+ | Install it | [`operations/install.md`](docs/operations/install.md) |
138
+ | Point it at a cluster | [`operations/onboarding-a-cluster.md`](docs/operations/onboarding-a-cluster.md) |
139
+ | Run a diagnosis and read the answer | [`operations/operator-runbook.md`](docs/operations/operator-runbook.md) |
140
+ | Run it unattended, or over the network | [`operations/continuous.md`](docs/operations/continuous.md) · [`operations/api.md`](docs/operations/api.md) |
141
+
142
+ ## Design
143
+
144
+ - Decisions: [`docs/architecture/adr/`](docs/architecture/adr/) — ADR-0038 and ADR-0039
145
+ define the loop; ADR-0113 records why a sense is a node in the estate graph rather than a
146
+ vote for a culprit.
147
+ - Architecture: [`system-architecture.md`](docs/architecture/system-architecture.md)
148
+ (structure) · [`incident-loop.md`](docs/architecture/incident-loop.md) (behaviour) ·
149
+ [`data-plane.md`](docs/architecture/data-plane.md)
150
+ - Estates: [`deploy/estates/`](deploy/estates/) — eighteen deployable fault estates, each
151
+ with its own contract and offline validators.
152
+ - Research: [`docs/research/`](docs/research/) · beliefs that constrain the design live in
153
+ [`_meta/beliefs.md`](docs/research/_meta/beliefs.md).
154
+
155
+ ## Contributing
156
+
157
+ ```bash
158
+ make help # every target
159
+ make check # the merge gate: lint, imports, types, dead code, tests + coverage, security, estates
160
+ ```
161
+
162
+ See [`CLAUDE.md`](CLAUDE.md) (= `AGENTS.md`) and [`CONTRIBUTING.md`](CONTRIBUTING.md). The
163
+ hard rule: no detection, localization, RCA or mitigation capability is "done" without an
164
+ end-to-end run that closes the telemetry loop — re-reading real cluster state to verify the
165
+ outcome, not the agent's self-report.
166
+
167
+ ## License
168
+
169
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,221 @@
1
+ [build-system]
2
+ # hatch-vcs (ADR-0205 D3): the version IS the git tag, never a hand-edited constant. A released
3
+ # artifact reporting 0.0.0 is a falsehood the release workflow would publish under a real tag.
4
+ # ⚑ hatchling is BOUNDED, and the upper bound is load-bearing. `[build-system].requires` is the one
5
+ # input to a release that `uv.lock` does not lock, so an unpinned backend means the published
6
+ # artefact can change with zero commits to this repository -- which is exactly what happened.
7
+ # hatchling 1.32.0 (2026-08-11) began emitting `Metadata-Version: 2.5`, and the SHA-pinned
8
+ # `pypa/gh-action-pypi-publish` bundles a twine that rejects it, so `v0.1.2` built, version-checked
9
+ # and cut a GitHub Release, then died on upload with:
10
+ #
11
+ # InvalidDistribution: Invalid distribution metadata: '2.5' is not a valid metadata version
12
+ #
13
+ # MEASURED by building this project against each: 1.32.0 -> 2.5, 1.31.0 -> 2.4, 1.27.0 -> 2.4.
14
+ # Lift the cap when the publish action ships a twine that accepts 2.5 -- its newest release
15
+ # (v1.14.2, 2026-07-29) PREDATES hatchling 1.32.0, so bumping the action today fixes nothing.
16
+ # The cap is the honest fix; disabling the action's `verify_metadata` would be passing by declining.
17
+ requires = ["hatchling>=1.27,<1.32", "hatch-vcs>=0.5"]
18
+ build-backend = "hatchling.build"
19
+
20
+ [project]
21
+ name = "autoops-sre"
22
+ dynamic = ["version"]
23
+ description = "An agentic ops harness for AIOps — SRE, cloud, Kubernetes, observability, and security automation."
24
+ readme = "README.md"
25
+ requires-python = ">=3.12"
26
+ license = { text = "MIT" }
27
+ authors = [{ name = "Evangelos Pappas", email = "epappas@evalonlabs.com" }]
28
+ keywords = ["aiops", "sre", "agent", "kubernetes", "observability", "harness"]
29
+ classifiers = [
30
+ "Development Status :: 2 - Pre-Alpha",
31
+ "Intended Audience :: Developers",
32
+ "License :: OSI Approved :: MIT License",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Topic :: System :: Systems Administration",
35
+ ]
36
+ dependencies = [
37
+ "typer>=0.27.1",
38
+ "pydantic>=2.7",
39
+ "pyyaml>=6.0",
40
+ "opentelemetry-api>=1.43.0",
41
+ "opentelemetry-sdk>=1.44.0",
42
+ "anthropic>=0.121.0",
43
+ "openai>=2.53.0",
44
+ "cedarpy>=4.8.7,<5",
45
+ # ADR-0042: the SPKI telemetry pin (sandbox/pinned_http.py) parses the negotiated DER leaf and
46
+ # extracts its SubjectPublicKeyInfo -- X.509 parsing the stdlib does not provide. Promoted from
47
+ # the dev group (#109); a real, disclosed runtime cost, not "free".
48
+ # >=50 pins out CVE-2026-69247 (fixed in 50.0.0); the SPKI pin (ADR-0042) needs this at runtime.
49
+ "cryptography>=50",
50
+ # ADR-0208, `autoops serve`. ONE new dependency SUBTREE, declared as the four names this code
51
+ # actually imports: `mcp` already depends on starlette and uvicorn, so naming them costs nothing
52
+ # in the resolved graph and buys honesty -- `serve/api.py` imports starlette, `serve/commands.py`
53
+ # imports uvicorn, and a module that imports a package it does not declare is one upstream drop
54
+ # away from breaking.
55
+ # `mcp` is the protocol's REFERENCE implementation. Hand-rolling the MCP wire protocol for an
56
+ # agent-facing boundary is the higher risk, not the lower one: session ids, protocol-version
57
+ # negotiation and the Streamable-HTTP handshake are exactly where a hand-rolled server silently
58
+ # stops interoperating. Measured before adding: `pip-audit --strict` and `osv-scanner` are both
59
+ # green on the resulting lock, and the tree overlaps what autoOps already ships (pydantic,
60
+ # opentelemetry-api, cryptography, anyio, httpx).
61
+ # `websockets` is NOT optional and that was MEASURED, not assumed: without it,
62
+ # `uvicorn.config.Config(ws="auto").load()` resolves `ws_protocol_class = None` and the shipped
63
+ # `autoops serve http` refuses every websocket upgrade -- the stage stream would not exist. With
64
+ # it, the same call resolves `WebSocketsSansIOProtocol`.
65
+ "mcp>=2.1.1",
66
+ "starlette>=1.6.0",
67
+ "uvicorn>=0.52.4",
68
+ "websockets>=17.1",
69
+ ]
70
+
71
+ [project.scripts]
72
+ autoops = "autoops.cli:main"
73
+ autoctl = "autoops.autoctl:main"
74
+
75
+ [project.urls]
76
+ Repository = "https://github.com/epappas/autoops"
77
+
78
+ [dependency-groups]
79
+ dev = [
80
+ # ⚑ DECLARED, not inherited. `httpx` reached the tests transitively through `openai` 2.x and the
81
+ # 3.x bump dropped it, breaking collection in `test_openrouter_client.py`, `test_serve_api.py` and
82
+ # `test_serve_mcp.py` with `ModuleNotFoundError`. Every one of the nine dependency PRs was green
83
+ # on its own; the UNION was red. This is exactly the hazard the main dependency block's own
84
+ # comment names -- "a module that imports a package it does not declare is one upstream drop away
85
+ # from breaking" -- so the fix is to declare it rather than re-pin `openai`.
86
+ "httpx>=0.28",
87
+ "ruff>=0.16.2",
88
+ "mypy>=2.3.0",
89
+ "pytest>=9.1.1",
90
+ "pytest-cov>=5.0",
91
+ "bandit>=1.7",
92
+ "pip-audit>=2.7",
93
+ # ADR-0099: the import-layering gate (`.importlinter`, run by `make lint-imports`). Unlike
94
+ # semgrep below, this one BELONGS in the group: `lint-imports` resolves its root package
95
+ # through `sys.path`, so an isolated `uvx` environment would analyse whatever `autoops` it
96
+ # could find rather than this working tree. Membership makes it resolve the editable install.
97
+ # Measured before adding, against the precedent below: it pulls `click` and `grimp` into the
98
+ # graph -- `click` being the exact package semgrep held at a vulnerable pin -- and resolves
99
+ # click 8.4.2, not the 8.1.8 that carries PYSEC-2026-2132. `make security` is green with it.
100
+ # Pinned exactly because this tool is a GATE: layer parsing, `exhaustive` and
101
+ # `exclude_type_checking_imports` are version-dependent, so a silent minor bump would change
102
+ # what the contract means, not just how it runs.
103
+ "import-linter==2.14",
104
+ "pre-commit>=4.6.1",
105
+ "cyclonedx-bom>=7.3.1",
106
+ "types-pyyaml>=6.0.12.20260724",
107
+ # semgrep is NOT a project dependency -- it is a TOOL, and it is pinned and run in isolation by
108
+ # the Makefile (`uvx semgrep==...`), exactly as osv-scanner is (`go run ...@v2.3.8`). It was a dev
109
+ # dependency until #126: it pins `click~=8.1.8`, and click 8.1.8 carries PYSEC-2026-2132, so a
110
+ # non-shipping scanner was holding a CVE inside the project's own dependency graph and failing the
111
+ # gate it exists to serve. Even semgrep's latest release (1.169.0) still carries that pin, so no
112
+ # bump fixes it -- the tool simply does not belong in the graph.
113
+ # cryptography moved to [project].dependencies for ADR-0042 (the SPKI pin needs it at
114
+ # runtime); tests/test_token_minter.py and the pinned-transport tests keep using it from there.
115
+ ]
116
+
117
+ [tool.hatch.version]
118
+ # ADR-0205 D3. The version comes from the nearest `v*` tag; `autoops.__version__` reads it back out
119
+ # of the installed distribution's metadata at runtime.
120
+ source = "vcs"
121
+ # BOTH the describe glob AND the regex are constrained to `v*`, and BOTH are load-bearing. This
122
+ # tree carries 15 non-release tags -- `gate-combined-8dc7c554`, `archive/estate-gate-6ee92ef`,
123
+ # `rescue/worktree-prune-2026-08-25/...` -- and setuptools-scm's DEFAULT_DESCRIBE glob is
124
+ # `*[0-9]*`, which matches them.
125
+ #
126
+ # ⚑ MEASURED end to end on 2026-08-31, in a scratch repository holding `v0.1.0` and then
127
+ # `gate-combined-8dc7c554` one commit later, exactly the shape a merge would produce here:
128
+ #
129
+ # git describe --tags --long --match '*[0-9]*' -> gate-combined-8dc7c554-0-g528b1e0
130
+ # git describe --tags --long --match 'v[0-9]*' -> v0.1.0-1-g528b1e0
131
+ # uv build, WITH these two lines -> autoops-0.1.1.dev1+g528b1e09-py3-none-any.whl
132
+ # uv build, with the defaults -> ValueError: Error getting the version from source
133
+ # `vcs`: Can't parse version from tag
134
+ # 'gate-combined-8dc7c554'
135
+ #
136
+ # So the default configuration REFUSES rather than lying, which is the better of the two failures
137
+ # -- but it refuses at release time, on a tag push, with the release half-made. `tag-pattern`
138
+ # alone cannot prevent it: `git describe` has already chosen the wrong tag by the time the regex
139
+ # sees the result, and a tag the regex rejects is an error, not a fallback.
140
+ #
141
+ # A LATENT trap today, not an active one: `git tag --merged HEAD` is empty, so none of the 15 is
142
+ # reachable from this branch yet. It is closed now because the merge that makes one reachable will
143
+ # not announce itself.
144
+ tag-pattern = '^v(?P<version>[0-9]+\.[0-9]+\.[0-9]+.*)$'
145
+ raw-options = { git_describe_command = [
146
+ "git", "describe", "--dirty", "--tags", "--long", "--match", "v[0-9]*", "--abbrev=8",
147
+ ] }
148
+ # Used ONLY where there is neither VCS metadata nor sdist PKG-INFO -- the container build, whose
149
+ # context excludes `.git` (`.dockerignore`). Declared here so the fallback has exactly one home
150
+ # rather than being duplicated into the Dockerfile. `.github/workflows/release.yml` ASSERTS the
151
+ # built wheel carries the pushed tag, so this string can never silently reach PyPI.
152
+ fallback-version = "0.1.0.dev0"
153
+
154
+ [tool.hatch.build.targets.wheel]
155
+ packages = ["src/autoops"]
156
+
157
+ [tool.hatch.build.targets.sdist]
158
+ # ⚑ DENY BY DEFAULT, exactly as `.dockerignore` does for the image (ADR-0205 D2), and for the same
159
+ # reason. The wheel target above bounds the wheel; nothing bounded the sdist, and hatchling's
160
+ # default sdist is the whole project directory. Measured before this section existed:
161
+ #
162
+ # autoops_sre-*.tar.gz 120,019,386 bytes vs the wheel's 1,590,905
163
+ # evidence/ 1,934 entries · artifacts/ 1,677 · docs/ 1,321 · deploy/ 562 · tests/ 416 · src/ 222
164
+ # 393 files inside it carrying the production control-plane IP
165
+ #
166
+ # `uv build` builds the sdist and then builds the WHEEL FROM IT, so an over-narrow list here fails
167
+ # the build loudly rather than shipping something short. These are the same five paths the image
168
+ # ships, which keeps one answer to "what leaves this repository" instead of two.
169
+ include = ["/src", "/README.md", "/LICENSE", "/uv.lock"]
170
+
171
+ [tool.ruff]
172
+ line-length = 100
173
+ target-version = "py312"
174
+ # `operators/` is out-of-tree live-crossing code (NOT shipped in the wheel), held to the same lint
175
+ # bar as `src`/`tests` — see operators/__init__.py for why it lives outside `src/`.
176
+ src = ["src", "tests", "operators"]
177
+
178
+ [tool.ruff.lint]
179
+ select = ["E", "F", "W", "I", "N", "UP", "B", "S", "C4", "C90", "SIM", "RUF"]
180
+ # S101 (assert) is expected in tests; S603/S607 reviewed case-by-case when subprocess lands.
181
+
182
+ [tool.ruff.lint.per-file-ignores]
183
+ "tests/**" = ["S101"]
184
+
185
+ [tool.ruff.lint.mccabe]
186
+ max-complexity = 10
187
+
188
+ [tool.mypy]
189
+ python_version = "3.12"
190
+ strict = true
191
+ warn_unreachable = true
192
+ warn_redundant_casts = true
193
+ plugins = ["pydantic.mypy"]
194
+ # `operators/` (the out-of-`src/` live-crossing transport) is type-checked strictly alongside `src`.
195
+ files = ["src", "operators"]
196
+
197
+ [[tool.mypy.overrides]]
198
+ # aiopslab is the cluster-only benchmark dependency, imported lazily by eval/runner.py and not
199
+ # installed in CI (it needs a live Kubernetes cluster). Don't fail strict mode on its absence.
200
+ module = ["aiopslab.*"]
201
+ ignore_missing_imports = true
202
+
203
+ [[tool.mypy.overrides]]
204
+ # cedarpy ships py.typed + inline annotations, so this is belt-and-braces in case a future
205
+ # release drops them. The Cedar PDP (policy/cedar_guard.py) is the only importer.
206
+ module = ["cedarpy.*"]
207
+ ignore_missing_imports = true
208
+
209
+ [tool.pytest.ini_options]
210
+ testpaths = ["tests"]
211
+ # Repo root on the path so tests can import the out-of-`src/` `operators` package. Coverage stays
212
+ # scoped to `autoops` (below), so operator code is exercised but never counts toward the 90% gate.
213
+ pythonpath = ["."]
214
+ addopts = "--cov=autoops --cov-report=term-missing --cov-fail-under=90 -q"
215
+
216
+ [tool.coverage.run]
217
+ source = ["autoops"]
218
+ branch = true
219
+
220
+ [tool.bandit]
221
+ exclude_dirs = ["tests"]