cloudfall 0.5.1__tar.gz → 0.6.0rc2__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 (312) hide show
  1. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/ARCHITECTURE.md +4 -3
  2. cloudfall-0.6.0rc2/CHANGELOG.md +593 -0
  3. cloudfall-0.6.0rc2/CODE_OF_CONDUCT.md +83 -0
  4. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/CONTRIBUTING.md +13 -0
  5. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/MANIFESTO.md +2 -4
  6. cloudfall-0.6.0rc2/PKG-INFO +259 -0
  7. cloudfall-0.6.0rc2/README.md +231 -0
  8. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/ROADMAP.md +7 -4
  9. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/Taskfile.yml +5 -5
  10. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/brownfield-design.md +1 -1
  11. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/fleet-goals.md +2 -2
  12. cloudfall-0.6.0rc2/docs/getting-started.md +149 -0
  13. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/landscape.md +4 -4
  14. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/logging-service-guide.md +10 -10
  15. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/neon-migration-guide.md +10 -10
  16. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/operator-guide.md +25 -14
  17. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/render-migration-guide.md +9 -8
  18. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/secrets-guide.md +6 -6
  19. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/README.md +26 -2
  20. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_time/tasks/main.yml +33 -1
  21. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/artifact.py +38 -29
  22. cloudfall-0.6.0rc2/engine/src/cloudfall_engine/cli.py +473 -0
  23. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/playbook.py +2 -12
  24. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_ansible_inventory.py +47 -11
  25. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_artifact_builder.py +62 -23
  26. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_playbook.py +126 -8
  27. cloudfall-0.6.0rc2/engine/tests/test_time_role.py +119 -0
  28. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/render.yaml +1 -1
  29. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/pyproject.toml +25 -6
  30. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/README.md +28 -23
  31. cloudfall-0.6.0rc2/sdk/src/cloudfall/agent_server.py +449 -0
  32. cloudfall-0.6.0rc2/sdk/src/cloudfall/agent_tools.py +52 -0
  33. cloudfall-0.6.0rc2/sdk/src/cloudfall/app.py +3626 -0
  34. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/audit.py +4 -1
  35. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/authoring.py +15 -7
  36. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/catalog.py +3 -1
  37. cloudfall-0.6.0rc2/sdk/src/cloudfall/cli.py +21 -0
  38. cloudfall-0.6.0rc2/sdk/src/cloudfall/commands.py +587 -0
  39. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/dashboard_server.py +28 -2
  40. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/decision.py +5 -5
  41. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/importer.py +1 -1
  42. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/lifecycle.py +73 -40
  43. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/observe.py +33 -6
  44. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/operator.py +102 -17
  45. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/project.py +62 -23
  46. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/render_api.py +7 -3
  47. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/secrets.py +4 -4
  48. cloudfall-0.6.0rc2/sdk/src/cloudfall/why.py +436 -0
  49. cloudfall-0.6.0rc2/sdk/tests/test_agent_server.py +400 -0
  50. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_ansible_reader.py +12 -10
  51. cloudfall-0.6.0rc2/sdk/tests/test_app.py +180 -0
  52. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_arguments.py +35 -74
  53. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_audit.py +59 -5
  54. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_authoring.py +79 -5
  55. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_catalog.py +9 -7
  56. cloudfall-0.6.0rc2/sdk/tests/test_commands.py +112 -0
  57. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_dashboard_server.py +20 -0
  58. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_decision.py +16 -13
  59. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_importer.py +31 -4
  60. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_inventory.py +3 -2
  61. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_lifecycle.py +109 -25
  62. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_migrate.py +9 -5
  63. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_observe.py +56 -8
  64. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_operator.py +192 -1
  65. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_project.py +28 -25
  66. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_render_api.py +5 -4
  67. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_validation.py +14 -9
  68. cloudfall-0.6.0rc2/sdk/tests/test_why.py +227 -0
  69. cloudfall-0.5.1/.github/workflows/ci.yml +0 -53
  70. cloudfall-0.5.1/.github/workflows/release.yml +0 -78
  71. cloudfall-0.5.1/CHANGELOG.md +0 -248
  72. cloudfall-0.5.1/PKG-INFO +0 -418
  73. cloudfall-0.5.1/README.md +0 -392
  74. cloudfall-0.5.1/engine/src/cloudfall_engine/cli.py +0 -273
  75. cloudfall-0.5.1/evaluations/cloudfall/README.md +0 -43
  76. cloudfall-0.5.1/evaluations/cloudfall/environment.md +0 -61
  77. cloudfall-0.5.1/evaluations/cloudfall/findings.md +0 -26
  78. cloudfall-0.5.1/evaluations/cloudfall/issues.md +0 -78
  79. cloudfall-0.5.1/evaluations/cloudfall/linkedin.md +0 -33
  80. cloudfall-0.5.1/evaluations/cloudfall/readiness.md +0 -84
  81. cloudfall-0.5.1/evaluations/cloudfall/report-agent-dev.md +0 -1003
  82. cloudfall-0.5.1/evaluations/cloudfall/report-dev.md +0 -1048
  83. cloudfall-0.5.1/evaluations/cloudfall/report-index.md +0 -49
  84. cloudfall-0.5.1/evaluations/cloudfall/report-issues.md +0 -410
  85. cloudfall-0.5.1/evaluations/cloudfall/report-runtime.md +0 -74
  86. cloudfall-0.5.1/evaluations/cloudfall/trace.md +0 -1276
  87. cloudfall-0.5.1/evaluations/cloudfall/x.md +0 -45
  88. cloudfall-0.5.1/sdk/src/cloudfall/agent_tools.py +0 -868
  89. cloudfall-0.5.1/sdk/src/cloudfall/arguments.py +0 -92
  90. cloudfall-0.5.1/sdk/src/cloudfall/cli.py +0 -2138
  91. cloudfall-0.5.1/sdk/src/cloudfall/commands.py +0 -294
  92. cloudfall-0.5.1/sdk/src/cloudfall/fleet_tools.py +0 -249
  93. cloudfall-0.5.1/sdk/src/cloudfall/mcp_server.py +0 -944
  94. cloudfall-0.5.1/sdk/tests/test_agent_tools.py +0 -362
  95. cloudfall-0.5.1/sdk/tests/test_commands.py +0 -91
  96. cloudfall-0.5.1/sdk/tests/test_fleet_tools.py +0 -301
  97. cloudfall-0.5.1/site/public/apple-touch-icon.png +0 -0
  98. cloudfall-0.5.1/site/public/favicon-32.png +0 -0
  99. cloudfall-0.5.1/site/public/icon-192.png +0 -0
  100. cloudfall-0.5.1/site/public/icon-512.png +0 -0
  101. cloudfall-0.5.1/site/public/index.html +0 -491
  102. cloudfall-0.5.1/site/public/manifesto.html +0 -367
  103. cloudfall-0.5.1/site/public/og.png +0 -0
  104. cloudfall-0.5.1/site/public/site.webmanifest +0 -23
  105. cloudfall-0.5.1/site/src/worker.js +0 -10
  106. cloudfall-0.5.1/site/wrangler.jsonc +0 -18
  107. cloudfall-0.5.1/uv.lock +0 -1198
  108. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/.ansible-lint +0 -0
  109. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/.gitignore +0 -0
  110. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/LICENSE +0 -0
  111. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/SECURITY.md +0 -0
  112. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/README.md +0 -0
  113. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/alert-rules/postgresql-down.yaml +0 -0
  114. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/applications/crm.yaml +0 -0
  115. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/components/crm-backend.yaml +0 -0
  116. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/domains/crm-site.yaml +0 -0
  117. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/logging-stacks/operations.yaml +0 -0
  118. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/operator-policies/production.yaml +0 -0
  119. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/server-types/debian-application.yaml +0 -0
  120. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/servers/h1.yaml +0 -0
  121. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/servers/h2.yaml +0 -0
  122. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/services/postgresql-main.yaml +0 -0
  123. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/services/redis-cache.yaml +0 -0
  124. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/examples/ssh-public-keys/example-admin.yaml +0 -0
  125. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/alert-rule.schema.json +0 -0
  126. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/application.schema.json +0 -0
  127. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/artifact.schema.json +0 -0
  128. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/backup-receipt.schema.json +0 -0
  129. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/common.schema.json +0 -0
  130. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/component.schema.json +0 -0
  131. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/deployment-receipt.schema.json +0 -0
  132. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/domain.schema.json +0 -0
  133. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/environment-receipt.schema.json +0 -0
  134. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/logging-stack.schema.json +0 -0
  135. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/observed-domain.schema.json +0 -0
  136. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/observed-server.schema.json +0 -0
  137. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/operation-decision.schema.json +0 -0
  138. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/operation.schema.json +0 -0
  139. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/operator-policy.schema.json +0 -0
  140. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/operator-proposal.schema.json +0 -0
  141. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/release-receipt.schema.json +0 -0
  142. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/server-type.schema.json +0 -0
  143. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/server.schema.json +0 -0
  144. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/service.schema.json +0 -0
  145. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/schemas/v1/ssh-public-key.schema.json +0 -0
  146. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/invalid/missing-server/applications/crm.yaml +0 -0
  147. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/invalid/missing-server/components/crm-backend.yaml +0 -0
  148. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/invalid/missing-server/server-types/debian-application.yaml +0 -0
  149. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/invalid/missing-server/servers/h1.yaml +0 -0
  150. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/invalid/schema/components/component-unknown-field.yaml +0 -0
  151. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/observed/compliant/h1.json +0 -0
  152. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/config/tests/observed/compliant/h2.json +0 -0
  153. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/ansible-context.md +0 -0
  154. /cloudfall-0.5.1/site/public/favicon.svg → /cloudfall-0.6.0rc2/docs/assets/logo.svg +0 -0
  155. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/availability-design.md +0 -0
  156. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/decisions/2026-09-14-no-ansible-replacement.md +0 -0
  157. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/decisions/2026-09-14-no-nixos-base.md +0 -0
  158. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/decisions/2026-09-14-no-rust-rewrite.md +0 -0
  159. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/hybrid-storage-design.md +0 -0
  160. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/new-server-storage-guide.md +0 -0
  161. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-debian-container-m1.md +0 -0
  162. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-dns-cutover.md +0 -0
  163. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-guided-data-migration.md +0 -0
  164. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-hetzner-m1.md +0 -0
  165. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-hetzner-m2-m3.md +0 -0
  166. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-hetzner-m4-m5.md +0 -0
  167. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-08-render-cutover.md +0 -0
  168. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-10-hetzner-m7.md +0 -0
  169. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-10-hetzner-m8.md +0 -0
  170. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-10-hetzner-m9.md +0 -0
  171. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/docs/proving-runs/2026-09-11-hetzner-m10.md +0 -0
  172. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/ansible.cfg +0 -0
  173. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/backup.yml +0 -0
  174. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/baseline.yml +0 -0
  175. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/bootstrap.yml +0 -0
  176. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/data.yml +0 -0
  177. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/deploy.yml +0 -0
  178. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/domains.yml +0 -0
  179. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/health.yml +0 -0
  180. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/inspect.yml +0 -0
  181. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/logging.yml +0 -0
  182. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/restart.yml +0 -0
  183. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/rollback.yml +0 -0
  184. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/services.yml +0 -0
  185. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/playbooks/time.yml +0 -0
  186. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_access/defaults/main.yml +0 -0
  187. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_access/handlers/main.yml +0 -0
  188. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_access/meta/argument_specs.yml +0 -0
  189. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_access/meta/main.yml +0 -0
  190. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_access/tasks/main.yml +0 -0
  191. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_bootstrap/defaults/main.yml +0 -0
  192. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_bootstrap/meta/argument_specs.yml +0 -0
  193. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_bootstrap/meta/main.yml +0 -0
  194. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_bootstrap/tasks/main.yml +0 -0
  195. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_data_migration/defaults/main.yml +0 -0
  196. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_data_migration/meta/argument_specs.yml +0 -0
  197. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_data_migration/meta/main.yml +0 -0
  198. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_data_migration/tasks/main.yml +0 -0
  199. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/defaults/main.yml +0 -0
  200. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/meta/argument_specs.yml +0 -0
  201. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/meta/main.yml +0 -0
  202. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/health.yml +0 -0
  203. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/health_gate.yml +0 -0
  204. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/main.yml +0 -0
  205. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/restart.yml +0 -0
  206. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/tasks/rollback.yml +0 -0
  207. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_deploy/templates/component.service.j2 +0 -0
  208. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/defaults/main.yml +0 -0
  209. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/handlers/main.yml +0 -0
  210. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/meta/argument_specs.yml +0 -0
  211. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/meta/main.yml +0 -0
  212. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/tasks/main.yml +0 -0
  213. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_firewall/templates/nftables.conf.j2 +0 -0
  214. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_inspect/defaults/main.yml +0 -0
  215. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_inspect/meta/argument_specs.yml +0 -0
  216. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_inspect/meta/main.yml +0 -0
  217. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_inspect/tasks/main.yml +0 -0
  218. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/defaults/main.yml +0 -0
  219. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/handlers/main.yml +0 -0
  220. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/meta/argument_specs.yml +0 -0
  221. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/meta/main.yml +0 -0
  222. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/tasks/main.yml +0 -0
  223. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager-defaults.j2 +0 -0
  224. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/alertmanager.yaml.j2 +0 -0
  225. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/grafana.ini.j2 +0 -0
  226. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-datasource.yml.j2 +0 -0
  227. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki-systemd-override.conf.j2 +0 -0
  228. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/loki.yaml.j2 +0 -0
  229. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/nginx-loki-gateway.conf.j2 +0 -0
  230. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-datasource.yml.j2 +0 -0
  231. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-defaults.j2 +0 -0
  232. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus-rules.yaml.j2 +0 -0
  233. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_backend/templates/prometheus.yaml.j2 +0 -0
  234. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/defaults/main.yml +0 -0
  235. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/handlers/main.yml +0 -0
  236. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/meta/argument_specs.yml +0 -0
  237. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/meta/main.yml +0 -0
  238. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/tasks/main.yml +0 -0
  239. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/templates/alloy-systemd-override.conf.j2 +0 -0
  240. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_logging_collector/templates/config.alloy.j2 +0 -0
  241. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/defaults/main.yml +0 -0
  242. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/handlers/main.yml +0 -0
  243. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/meta/argument_specs.yml +0 -0
  244. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/meta/main.yml +0 -0
  245. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/tasks/main.yml +0 -0
  246. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_nginx_site/templates/domain-site.conf.j2 +0 -0
  247. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/defaults/main.yml +0 -0
  248. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/handlers/main.yml +0 -0
  249. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/meta/argument_specs.yml +0 -0
  250. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/meta/main.yml +0 -0
  251. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/tasks/main.yml +0 -0
  252. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.service.j2 +0 -0
  253. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.sh.j2 +0 -0
  254. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/backup.timer.j2 +0 -0
  255. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/cloudfall.conf.j2 +0 -0
  256. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.service.j2 +0 -0
  257. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.sh.j2 +0 -0
  258. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_postgresql/templates/restore-check.timer.j2 +0 -0
  259. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/defaults/main.yml +0 -0
  260. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/handlers/main.yml +0 -0
  261. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/meta/argument_specs.yml +0 -0
  262. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/meta/main.yml +0 -0
  263. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/tasks/main.yml +0 -0
  264. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.service.j2 +0 -0
  265. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.sh.j2 +0 -0
  266. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/backup.timer.j2 +0 -0
  267. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/cloudfall.conf.j2 +0 -0
  268. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.service.j2 +0 -0
  269. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.sh.j2 +0 -0
  270. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_redis/templates/restore-check.timer.j2 +0 -0
  271. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_time/defaults/main.yml +0 -0
  272. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_time/meta/argument_specs.yml +0 -0
  273. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_time/meta/main.yml +0 -0
  274. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/defaults/main.yml +0 -0
  275. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/argument_specs.yml +0 -0
  276. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/meta/main.yml +0 -0
  277. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/ansible/roles/cloudfall_unattended_upgrades/tasks/main.yml +0 -0
  278. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/__init__.py +0 -0
  279. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/__main__.py +0 -0
  280. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/ansible_inventory.py +0 -0
  281. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/src/cloudfall_engine/py.typed +0 -0
  282. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_deploy_templates.py +0 -0
  283. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_firewall_templates.py +0 -0
  284. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_logging_templates.py +0 -0
  285. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_nginx_site_templates.py +0 -0
  286. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/engine/tests/test_postgresql_templates.py +0 -0
  287. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/README.md +0 -0
  288. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/pyproject.toml +0 -0
  289. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/src/demo/__init__.py +0 -0
  290. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/src/demo/asgi.py +0 -0
  291. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/examples/render-demo/uv.lock +0 -0
  292. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/__init__.py +0 -0
  293. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/__main__.py +0 -0
  294. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/ansible_api.py +0 -0
  295. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/ansible_reader.py +0 -0
  296. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/cutover.py +0 -0
  297. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/dashboard.py +0 -0
  298. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/domain.py +0 -0
  299. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/inventory.py +0 -0
  300. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/migrate.py +0 -0
  301. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/observation.py +0 -0
  302. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/operations.py +0 -0
  303. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/py.typed +0 -0
  304. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/resources.py +0 -0
  305. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/service_evidence.py +0 -0
  306. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/src/cloudfall/validation.py +0 -0
  307. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_cutover.py +0 -0
  308. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_data_migration.py +0 -0
  309. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_operations_dashboard.py +0 -0
  310. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_resources.py +0 -0
  311. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_secrets.py +0 -0
  312. {cloudfall-0.5.1 → cloudfall-0.6.0rc2}/sdk/tests/test_service_evidence.py +0 -0
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Cloudfall is the operator's record for a small fleet run by an AI agent.
3
+ Cloudfall is the operator's record for self-hosted servers run by an AI agent.
4
4
  The agent decides, Ansible executes, and Cloudfall sits between them: it
5
5
  declares what the agent may do, checks every result, and keeps the evidence
6
6
  behind every decision. Ansible is the hand, the agent is the brain,
@@ -15,7 +15,8 @@ Debian servers without Kubernetes (see the [fleet goals](docs/fleet-goals.md)),
15
15
  on a config the team already owns. This document describes the durable
16
16
  design; [`ROADMAP.md`](ROADMAP.md) is the authoritative source for what is
17
17
  implemented today, and the [brownfield design](docs/brownfield-design.md)
18
- describes the proposed next step, none of which is built.
18
+ describes the next step, whose operations catalog, gate and record are
19
+ built while safe edits and autonomy from the record are not.
19
20
 
20
21
  Every layer keeps the same invariants: no action without declared config, no
21
22
  status without evidence, no compliance without audit, no decision without a
@@ -32,7 +33,7 @@ Human / AI agent ──runs───► CLI / MCP / Python API ──► Gate
32
33
 
33
34
  The config is a store, not a pipeline stage: humans and agents edit it
34
35
  directly, the CLI validates and reads it, and the engine converges servers
35
- to it. The agent operates through a stable CLI, the `cloudfall-mcp` server
36
+ to it. The agent operates through a stable CLI, the `cloudfall mcp serve` server
36
37
  and a Python API instead of inventing shell commands or discovering
37
38
  infrastructure over SSH. Every mutating operation goes through the engine's
38
39
  explicit playbook contracts, waits behind a gate the agent does not control
@@ -0,0 +1,593 @@
1
+ # Changelog
2
+
3
+ Notable changes to Cloudfall. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
+ [semantic versioning](https://semver.org/) once the project reaches 1.0.
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - `cloudfall why` answers "why did the agent do that" from the decision
12
+ records alone, for a host (`--host`), an operation (`--operation`) or a
13
+ time window (`--since`, `--until`, ISO 8601 or a bare date), as JSON or
14
+ as one HTML page (`--format html`). Each decision is told as what it was
15
+ based on, what was proposed, what check mode showed, what gated it, who
16
+ approved, what the run and the verify step did and how it ended, every
17
+ sentence drawn from a field of the record. A decision is about a host
18
+ when the record names it, as the target or in the per-host evidence of
19
+ a stage; a group pattern is not expanded, because that would need an
20
+ inventory the record does not depend on. The same question is the
21
+ read-only `why` tool on `cloudfall-mcp --repository`
22
+ - Every JSON document `cloudfall` prints, results and errors alike, carries
23
+ `meta.schema_version` (`MAJOR.MINOR` of the output contract, now `1.0`)
24
+ and `meta.tool_version` (the installed package version), so an agent can
25
+ tell when the output shape may have changed without a separate call.
26
+ `meta.request_id` names the invocation with a UUID, the same on every
27
+ document it writes, for quoting in a retry or a bug report, and
28
+ `meta.duration_ms` counts the milliseconds from the start of the command
29
+ to that document, not counting Python's own startup.
30
+ MINOR moves when a command gains a key, MAJOR when one is removed,
31
+ renamed or changes meaning. `cloudfall --version` prints the same
32
+ version as JSON and exits 0
33
+ - Every JSON document also carries a `warnings` list. A key is removed only
34
+ after a release in which documents holding it warn `FIELD_DEPRECATED`
35
+ with the replacement key and the schema version that drops it
36
+ - `cloudfall changelog [--since MAJOR.MINOR]` lists changes to the output
37
+ contract, newest first: version, date, `breaking`, and the added,
38
+ removed and changed keys
39
+ - `cloudfall --schema-version MAJOR <command>` pins the output contract: it
40
+ fails with `invalid_argument` before the command runs when this build no
41
+ longer writes that MAJOR, so a pinned caller stops at a breaking release
42
+ instead of misreading it. `--version` and `changelog` report the current
43
+ and minimum supported versions
44
+ - `cloudfall --schema` (alias `--print-schema`) prints every command in one
45
+ JSON document: its flags, read from the parser; its effect and gate, from
46
+ the command catalog; and an `output_schema` per command, a JSON Schema of
47
+ each stdout shape with the condition that selects it. Every key, at the
48
+ top level and under `data`, carries `x-stability`: `stable` keys change
49
+ only in a MAJOR release after
50
+ a deprecation warning, `experimental` keys (unreleased commands such as
51
+ `why` and `changelog`) may change in any release. What each key holds is
52
+ not declared yet. The tests validate real output against these schemas,
53
+ and an `etag` changes only when a command, flag or declared output does
54
+
55
+ - Every error carries `exit_code`, the code the process exits with, so a
56
+ caller that reads the JSON needs no second channel: `{"code",
57
+ "message", "exit_code"}`. Results that exit non-zero, such as an
58
+ `audit` that finds drift, say so with `ok: false` and keep `error`
59
+ `null`
60
+ - Every `cloudfall` `--help` ends with the exit codes and what each one
61
+ means: 0 positive, 1 ran with a negative result, 2 nothing ran, 3
62
+ unknown. The generated `AGENTS.md` renders the same table from the same
63
+ source, so the two cannot drift
64
+ - `--quiet` writes nothing to stderr: no error document, no help text, and
65
+ no request lines from `dashboard serve`. The exit code still says what
66
+ happened, and results still go to stdout
67
+ - `--warnings-as-errors` fails a result that carries a warning: the
68
+ document says `ok: false` with the error `warnings_as_errors`, its
69
+ `status` and `data` unchanged, and the command exits 1 instead of 0
70
+
71
+ ### Changed
72
+
73
+ - The README, the site, the roadmap and the architecture say what is built
74
+ on a team's own Ansible repository (the operations catalog, the gate,
75
+ decision records and `cloudfall why`) instead of calling it unbuilt, show
76
+ a real `cloudfall why` answer, and address both teams leaving a PaaS and
77
+ teams already running Ansible
78
+ - The README opens with what Cloudfall is, a diagram of the gate, a short
79
+ quickstart and a guide index; the long walkthroughs moved to
80
+ [docs/getting-started.md](docs/getting-started.md). The repository has issue
81
+ forms (bug, feature, pilot request), a pull request template, a support
82
+ page and a code of conduct, and the PyPI summary matches the project's description
83
+ - The repository moved to the `cloudfall-dev` organization:
84
+ github.com/cloudfall-dev/cloudfall. The package metadata, the README,
85
+ the site and `REPOSITORY_URL` point there; the old
86
+ github.com/romamo/cloudfall address redirects
87
+ - Releases are cut by [shipmill](https://github.com/shipmill/shipmill) from
88
+ this CHANGELOG: an rc (`0.6.0rc1`, ...) after each batch of merges, and a
89
+ stable release promoted from an rc that soaked 3 days once its milestone
90
+ closes. The source distribution leaves out the repository's tooling,
91
+ evaluations and website
92
+ - **Breaking:** the CLI follows one name per meaning, and its output is
93
+ typed:
94
+ - Decision records have their own group: `decisions list` (was
95
+ `operations decisions`), `decisions approve` (was `operations
96
+ approve`), and a new `decisions show`. `operations` keeps the catalog:
97
+ `list`, `show`, `propose`
98
+ - `data migrate` is `data copy`, no longer read as `migrate`, and
99
+ `services inspect` is `services observe`, as `observe` collects server
100
+ evidence. Each old path answers 13 `REDIRECTED` with the new command in
101
+ `error.redirect.command`
102
+ - `migrate --release` and `--env-file` are `--component-release` and
103
+ `--component-env-file`: they take `COMPONENT=VALUE` pairs, where
104
+ `deploy`'s take one value
105
+ - `--observed` defaults to `tmp/observed` on every command, where
106
+ `observe` writes: `audit`, `services status` and `dashboard` required
107
+ it, and the operator's drift checks used `tmp/operator/observed`
108
+ - Field names in `data` are snake_case at every depth, as treaty's own
109
+ envelope keys are (`openProposals` is `open_proposals`, `wouldRun` is
110
+ `would_run`). Map keys that are data keep their spelling (resource
111
+ kinds, environment variable names, ids), and a record or resource in
112
+ `data`, anything with an `apiVersion`, keeps its stored form
113
+ - Every command's output schema gives each `data` key its JSON type, and
114
+ an optional list or object is written empty rather than `null`
115
+ (`inventory show` writes `ansible` as `{}` for a project)
116
+ - **Breaking:** argparse is gone: the last three `cloudfall` commands
117
+ move to treaty, and so do root `--help`, `--version` and `--schema`.
118
+ - `operator run` streams one envelope line per pass as it ends, with
119
+ `data.pass` (`alerts`, `drift`, `autonomy`) and `data.effect`:
120
+ `created` for a pass that wrote proposals, `updated` for one that ran
121
+ licensed proposals, `noop` otherwise. Stopping it ends the stream with a
122
+ `CANCELLED` line and exit 130 (SIGINT) or 143 (SIGTERM), so a systemd
123
+ unit needs `SuccessExitStatus=143` (the
124
+ [operator guide](docs/operator-guide.md) has it). A gateway it cannot
125
+ use (none declared, or a missing or unreadable certificate) is 4
126
+ `PRECONDITION`, an unreachable one 12 `UNAVAILABLE`. `--interval` and `--drift-interval` must be above 0. The
127
+ operating contract now lists it with the commands that change servers,
128
+ since a declared `OperatorPolicy` lets it run proposals
129
+ - `dashboard serve` streams one line once the server listens, then serves
130
+ until stopped, ending like `operator run`. Its request lines are info
131
+ log lines on stderr, shown at a terminal or with `--verbose`
132
+ - `changelog` is treaty's: it lists interface changes per release from
133
+ the packaged `schema-changelog.json`, which `treaty changelog-add
134
+ cloudfall.app:app` writes, and `--since` takes a release version
135
+ - `operator approve` reports such a gateway as 4 `PRECONDITION` (was 86
136
+ `RECORD_INVALID`) and an unreachable one as 12 `UNAVAILABLE`
137
+ - The argparse-era output contract added earlier in this release is
138
+ gone: the six-key document with a top-level `status`, `--output json`,
139
+ `--schema-version MAJOR` against that contract, the parser-built
140
+ `--schema`, the `--help` exit-code table and exit codes 0 to 3. Every
141
+ command answers treaty's envelope, and `cloudfall --schema` lists every
142
+ command's flags, output schema and exit codes. The `AGENTS.md` that
143
+ `cloudfall init` writes describes this contract
144
+ - **Breaking:** `cloudfall-mcp` is gone; the agent server is `cloudfall mcp
145
+ serve` (treaty 1.0.0rc29, from issues #239, #240, #281 and #285 filed for
146
+ it). Its tools are Cloudfall's own commands, answering with the CLI's
147
+ envelope, and tool names follow the commands (`list_operations` is
148
+ `operations_list`, `validate_config` is `config_validate`). MCP client
149
+ configs change from `cloudfall-mcp …` to `cloudfall mcp serve …`
150
+ - The server serves one fleet, chosen as before: `--project`, or
151
+ `--repository` for a team's Ansible repository. The fleet, the schema
152
+ and engine directories, and every directory a tool writes (snapshots,
153
+ receipts, artifacts, proposals, import output, the migrate plan) are
154
+ fixed for the run: they leave the tool schemas, and a call that passes
155
+ one is refused, so no call reaches another fleet or engine or writes
156
+ outside the project. So are the Render API URL `import_render-api`
157
+ sends its key file to, the env file `secrets_render` writes, and the
158
+ alert gateway `operator_approve` verifies against. Files a tool reads
159
+ as input (a blueprint, a key file, an env file) stay arguments
160
+ - On a project, `deploy`, `rollback`, `restart`, `migrate` and
161
+ `data_migrate` return their plan until called with `yes: true`, as on
162
+ the CLI; the old `confirm=true` handshake is gone. `build_artifact` and
163
+ the `converge_baseline`, `converge_services` and `converge_domains`
164
+ tools stay, the converges destructive and run only with
165
+ `confirm_destructive: true`. `operator_approve`, `backup_run` and
166
+ `backup_verify` run in one call, as their commands do (they were
167
+ confirm-gated in `cloudfall-mcp`), and `operator_watch` is gone: the
168
+ watch loop runs as a service
169
+ - On a repository, the tools are the read-only fleet commands and one
170
+ `operation_*` tool per declared operation, which runs check mode,
171
+ records a proposal and answers with the approval command in
172
+ `data.next`; a destructive operation's tool asks for
173
+ `confirm_destructive` even to preview
174
+ - Each directory has one flag name across the commands that read and
175
+ write it, so a server can fix it once and a non-default one reaches
176
+ them all: `observe --output-dir` is `--observed` (as `audit` reads it),
177
+ `services inspect --output-dir` is `--service-observed` (as `services
178
+ status` reads it), and `--receipts` is `--releases` on `deploy` and
179
+ `migrate`, `--backups` on `backup run|verify`, `--data-migrations` on
180
+ `data migrate` and `--env-receipts` on `secrets render` (as `audit`
181
+ reads it). `mcp serve` takes `--releases`, `--backups` and
182
+ `--data-migrations` too
183
+ - `init`, `operations approve`, `operator run` and `dashboard serve` are
184
+ never MCP tools (`mcp=False`), on this server or `treaty-mcp`
185
+ - A fleet that cannot be read stops the server before a client connects,
186
+ with the envelope on stderr: 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID`,
187
+ 85 `INVENTORY_UNREADABLE`. `--list-tools` prints the tool list
188
+ - `dashboard serve` reports a port it cannot bind as 4 `PRECONDITION`
189
+ with `dashboard_listen_failed`; an unreadable evidence file is no
190
+ longer reported as a listen failure
191
+ - **Breaking:** `cloudfall-engine` runs on [treaty](https://github.com/romamo/treaty).
192
+ Every command answers one JSON envelope on stdout (`ok`, `data`, `error`,
193
+ `warnings`, `meta`), errors included, which used to go to stderr in their
194
+ own shape; `inventory render` puts the inventory under `data.inventory`
195
+ (the `--output` file is unchanged). Failures exit with their own codes:
196
+ 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID`, 81 `ARTIFACT_BUILD_FAILED`,
197
+ 82 `PLAYBOOK_FAILED`; the old snake_case code is in `error.context.code`.
198
+ A missing playbook, inventory, or role directory exits 2 before Ansible
199
+ starts. `playbook run --check` is now `--dry-run`; Ansible's play log
200
+ streams to stderr. `cloudfall-engine manifest` describes every command
201
+ - **Breaking:** the last eight commands that change state move to treaty:
202
+ `observe`, `secrets render`, `import render`, `import render-api`,
203
+ `deploy`, `rollback`, `restart` and `data migrate` (treaty #183 lets
204
+ their camelCase keys through). Each keeps its `data` keys, `status`
205
+ included; the ones that write add `effect`. `deploy`, `rollback`,
206
+ `restart` and `data migrate` still change nothing without `--yes`: that
207
+ run is treaty's dry run (`effect: would_update`, `meta.dry_run: true`,
208
+ treaty #197). The plan writes the result keys as `null`, and the result
209
+ writes the plan keys (`wouldRun`, `instruction`) as `null`, since treaty
210
+ answers one object per command. `observe` is read-only (it declares the
211
+ snapshots it writes under the project as output, kept by `cleanup`) and exits 91 `INCOMPLETE` (was 1)
212
+ with the run in `data` when a server gave no snapshot. Engine steps,
213
+ sops and the Render API go through treaty: `doctor` checks sops, and
214
+ `import render-api` honours `--proxy` and marks its answer as content
215
+ from outside. Failures that were exit 2 are 4 `PRECONDITION` with the
216
+ old code in `error.context.code`. A key a command writes only sometimes
217
+ is `null` when absent, so `inventory show` writes `data.ansible` as
218
+ `null` for a project
219
+ - **Breaking:** `why` runs on treaty, `--format html` included (treaty
220
+ #179): the page is drawn from the answer's JSON document, as before. Its
221
+ verdict moves to `data.status`; a bad `--since` or `--until` is 2
222
+ `ARG_ERROR` with `why_instant_invalid` in `error.context.code`, and a
223
+ record that cannot be read 86 `RECORD_INVALID`. `--format html` is a
224
+ `why` format only; other commands refuse it
225
+ - **Breaking:** seven commands that run Ansible move to treaty: `health`,
226
+ `backup run`, `backup verify`, `operations propose`, `operations approve`,
227
+ `operator approve` and `migrate`. Ansible runs through treaty in the
228
+ project directory, so `--timeout` and Ctrl-C stop it, and `doctor` checks
229
+ ansible-playbook. `operations approve` and `migrate` still change nothing
230
+ without `--yes`; that preview is treaty's dry run, `effect: would_update`
231
+ and `meta.dry_run: true`, with `data.status` `pending` or `plan`. New exit
232
+ codes, each with the result
233
+ in `data`: 87 `ENGINE_STEP_FAILED` (an inventory render or playbook
234
+ failed, was 1), 88 `UNHEALTHY` (was 1), 89 `CHECK_FAILED` when check mode
235
+ fails in `propose` (was 1), 90 `NOT_VERIFIED` when an approved run fails
236
+ or does not verify (was 1), 3 `PARTIAL_FAILURE` for a paused `migrate`.
237
+ A failed `migrate` step's own code and message move from the top-level
238
+ `error` to `error.context`. `deploy`, `rollback`, `restart` and
239
+ `data migrate` stay on argparse until treaty #183, since their plan
240
+ answer has a `wouldRun` key
241
+ - **Breaking:** six more `cloudfall` commands run on treaty: `init`,
242
+ `add ssh-key`, `add server-type`, `add server`, `dashboard build` and
243
+ `services inspect`. Their answer is the treaty envelope described below,
244
+ with the same `data` keys, plus `effect: created` on `init` and `add`,
245
+ which treaty requires of a command that changes the project.
246
+ `dashboard build` and `services inspect` stay read-only and declare the
247
+ reports they write under `tmp/` as their output, which treaty's `cleanup`
248
+ never removes. Failures: 6 `CONFLICT` for a resource that already
249
+ exists (was 2), 7 `PERMISSION_DENIED` for a read-only project or path
250
+ (was 1 or 2), 4 `PRECONDITION` for a non-empty `init` directory (was 2),
251
+ 5 `NOT_FOUND` for a missing key file, 2 `ARG_ERROR` for a bad id, name or
252
+ address; the old code is in `error.context.code`. `init` runs git through
253
+ treaty, so `cloudfall doctor` checks for git 2.24. `observe`,
254
+ `secrets render` and the two `import` commands stay on argparse until
255
+ treaty can carry their camelCase keys on a command that writes (#183)
256
+ - **Breaking:** nine read-only `cloudfall` commands run on treaty, the
257
+ first step of moving the CLI off argparse: `config validate`,
258
+ `inventory show`, `operations list`, `operations show`,
259
+ `operations decisions`, `operator list`, `operator show`, `audit` and
260
+ `services status`. `cloudfall manifest` lists them; every other command,
261
+ root `--help` and `--version` stay on argparse for now. Their answer is a
262
+ treaty envelope (`ok`, `data`, `error`, `meta`, `warnings`): the verdict
263
+ moves from the top-level `status` to `data.status`, and the other `data`
264
+ keys are unchanged. `inventory show` writes `data.ansible` as `null` for a
265
+ project. Failures are an envelope on stdout with their own exit codes:
266
+ 79 `PROJECT_INVALID`, 80 `CONFIG_INVALID` (resources or evidence files),
267
+ 85 `INVENTORY_UNREADABLE`, 86 `RECORD_INVALID`, 5 `NOT_FOUND` for an
268
+ undeclared operation or a missing proposal, 2 `ARG_ERROR` for bad input;
269
+ the old snake_case code is in `error.context.code`. `audit` exits 83
270
+ `DRIFT` (was 1) or 84 `UNKNOWN` (was 3) with the report in `data`. Pick
271
+ the format with `--format json`: `--output` after one of these commands
272
+ is refused, since treaty's `--output` names a file. Relative paths still
273
+ resolve against the project, without changing the working directory
274
+ - `cloudfall-engine` runs Ansible and git through treaty's `ctx.run`
275
+ (treaty 1.0.0rc18), so `--timeout` and Ctrl-C stop the whole process
276
+ group, and secrets are redacted from the play log. The play log streams
277
+ to stderr as plain text, as before, terminal or not; `--quiet` silences
278
+ it. A failed playbook carries the last 4096 characters of the log in
279
+ `error.context.output`, marked as untrusted content from the hosts.
280
+ `playbook run` reports `meta.dry_run: true` under `--dry-run`. A git step
281
+ that runs past its 600-second limit now exits 10 `TIMEOUT`, not 81 with
282
+ `artifact_git_timeout`. `cloudfall-engine doctor` checks for
283
+ ansible-playbook 2.21 and git 2.24
284
+ - Every JSON document has the same six top-level keys: `ok` (true exactly
285
+ when the exit code is 0), `status` (the command's verdict, such as `ok`,
286
+ `plan`, `drift` or `unhealthy`), `data` (the command's payload, which
287
+ used to sit at the top level beside `status`), `error` (`null` unless
288
+ the command failed), and `meta` and `warnings`. A failure on stderr is
289
+ `{"ok": false, "status": "error", "data": null, "error": {"code",
290
+ "message"}}`. An agent reads `ok` and `data` without
291
+ knowing which command it ran. Anything that read a payload key from the
292
+ top level, such as `.inventory`, now reads it under `data`
293
+ - `--help` prints to stderr when stdout is not a terminal, so a caller
294
+ capturing stdout never finds usage text where it expects JSON. At a
295
+ terminal it stays on stdout
296
+ - `--output json` is accepted before the command and after every command.
297
+ JSON is the only format, so the flag changes nothing; a caller that asks
298
+ for JSON the usual way no longer gets a usage error. `--output` with any
299
+ other value is `invalid_argument`
300
+ - The six options that named an output path are renamed so `--output`
301
+ means one thing everywhere: `--output-dir` on `observe`,
302
+ `services inspect`, `dashboard build`, `import render` and
303
+ `import render-api`, and `--output-file` on `secrets render`. Scripts
304
+ passing `--output DIR` to these commands must switch
305
+ - `operator` errors use the same envelope as every other command,
306
+ `{"status": "error", "error": {"code", "message"}}`, instead of a bare
307
+ `{"code", "message"}`, so one parser reads every failure
308
+ - `operator show` and `operator approve` print the receipt under
309
+ `data.proposal` with a top-level `status`, instead of the bare receipt.
310
+ `approve` reports `status: failed` when the verify step did not confirm
311
+ the fix, as the `cloudfall-mcp` tool already did
312
+
313
+ ### Fixed
314
+
315
+ - `migrate --env-file NAME=PATH` and `--data NAME=PATH` resolve a relative
316
+ path against the project again, as every other path does; since the
317
+ move to treaty they resolved against the current directory
318
+ - `migrate --build` and the `cloudfall-mcp` `build_artifact` tool read the
319
+ release from the engine's treaty envelope, under `data`. Since the engine
320
+ moved to treaty every build step failed with "artifact builder returned no
321
+ release id". `build_artifact` now returns the engine's `data` keys
322
+ (`git_ref`, `archive_sha256`, ...) beside `status`
323
+ - The time baseline accepts a host whose clock another daemon keeps
324
+ (ntp, ntpsec, chrony). `timedatectl set-ntp` drives systemd-timesyncd
325
+ only, so on such hosts check mode reported "Would enable network time
326
+ synchronization" and a deploy failed with "NTP not supported". The role
327
+ now switches NTP on only where systemd-timesyncd is installed, and
328
+ otherwise requires the clock to be synchronized; check mode warns when
329
+ nothing keeps time, and a deploy fails with a message naming the daemon
330
+ to check when the clock does not synchronize within 60 seconds
331
+ - `cloudfall add` into a read-only or full project failed with a Python
332
+ traceback and could leave a new server type behind when only the
333
+ server file failed. It now removes what it wrote and fails with
334
+ `project_write_failed` (exit 2), naming the file and the reason. Any
335
+ other command that cannot write a path because it is read-only or not
336
+ the caller's reports `path_not_writable` as JSON (exit 1, since part of
337
+ the work may be done) instead of a traceback
338
+ - `cloudfall observe` no longer prints Ansible's play log on stdout ahead
339
+ of its JSON document, which broke any caller parsing stdout as JSON.
340
+ The playbook's output is captured, and when the run fails its last 2000
341
+ characters, ending with the play recap, are kept in `data.detail`. The
342
+ same capture keeps the `observe` tool of `cloudfall-mcp` from writing
343
+ Ansible text into the protocol stream
344
+ - `cloudfall-mcp` `operator_watch` and `operator_approve` wrapped engine
345
+ failures in the error envelope twice (`error.error.code`); they now
346
+ return one envelope like every other tool
347
+ - A failed `operator approve` is recorded even when the engine's log is
348
+ long. The receipt keeps the error code and the end of the log, where the
349
+ failing task and the play recap are, within the 1000-character cap.
350
+ Before, a run against unreachable hosts crashed while writing the
351
+ `failed` receipt, left the proposal `proposed` with no trace that it ran,
352
+ and showed the caller a traceback
353
+
354
+ ## [0.5.1] - 2026-09-22
355
+
356
+ ### Changed
357
+
358
+ - Decision records default to `decisions/` beside the operations rather
359
+ than to `tmp/decisions`: the record is what a team keeps, and `tmp/` is
360
+ what they throw away. The diffs and logs beside each record are raw
361
+ Ansible output, so a repository that commits them wants `no_log` on the
362
+ tasks that handle secrets
363
+
364
+ ### Fixed
365
+
366
+ - A decision is `verified` only when its verify run changed nothing. A
367
+ verify step that exits zero while changing a host found the fleet not as
368
+ the run left it and converged it further, which verifies nothing, so it
369
+ is recorded `failed`. Every approved decision now carries a `verdict`
370
+ saying why it ended where it did
371
+ - A record cites its artifacts and its observation basis as the repository
372
+ sees them rather than by absolute path, because a record is committed
373
+ and one machine's home directory means nothing in anyone else's checkout
374
+
375
+ ## [0.5.0] - 2026-09-21
376
+
377
+ ## [0.4.0] - 2026-09-21
378
+
379
+ ### Added
380
+
381
+ - Brownfield fleet reader: `cloudfall audit` and `cloudfall inventory show`
382
+ read the fleet from the team's own Ansible inventory, with `--inventory`
383
+ or from the inventory an `ansible.cfg` in the current directory names.
384
+ Hosts, groups and connection settings come from Ansible; a `cloudfall`
385
+ block per host, `cloudfall_defaults` per group and the
386
+ `cloudfall_server_types` catalog carry what Ansible does not model. The
387
+ documents are validated against the same schemas a project directory
388
+ gets, so `PlatformInventory` and everything built on it is unchanged
389
+ - `cloudfall.ansible_api` is the only module that imports ansible-core. It
390
+ reads in process and falls back to the `ansible-inventory` command,
391
+ reporting which served the read
392
+ - `cloudfall observe` collects one read-only snapshot per server through
393
+ the team's inventory plus an ephemeral overlay holding only the
394
+ `cloudfall_servers` group and the two variables the inspect role cannot
395
+ derive, so the audit loop closes without a Cloudfall project. Their own
396
+ `ansible.cfg` keeps deciding how Ansible connects; only the roles path
397
+ is forced. `--limit` takes an Ansible host pattern, which Ansible
398
+ resolves, and the run is judged against the hosts it asked for
399
+ - `StateValidator.validate_documents` validates resource documents
400
+ assembled in memory, with the schemas, reference checks and error codes
401
+ documents on disk get
402
+ - The operations catalog: every playbook the team runs is declared in
403
+ `operations/` with a risk level, a target scope, typed inputs,
404
+ preconditions and a verify step, and `cloudfall operations list|show`
405
+ reads it. A playbook nobody declared is not an operation
406
+ - The gate and the record: `cloudfall operations propose` runs an
407
+ operation in check mode and records the operation, targets, inputs, the
408
+ evidence it was based on and the diff by sha256;
409
+ `cloudfall operations approve --yes` records the approver, runs it, runs
410
+ the verify playbook and closes the record as executed, verified or
411
+ failed; `cloudfall operations decisions` reads the trail without the
412
+ catalog
413
+ - The agent surface: `cloudfall-mcp --repository` serves a brownfield
414
+ repository, where the tool list is the catalog. Each declared operation
415
+ is one tool carrying its risk as MCP annotations, calling it runs check
416
+ mode and records a proposal, and no tool approves anything: that stays a
417
+ command a person runs
418
+
419
+ ## [0.3.0] - 2026-09-20
420
+
421
+ ### Removed
422
+
423
+ - `cloudfall init --rev` and `--source`, and the machinery behind them:
424
+ `GitRevision`, `GitSourceUrl`, `GitPin`, `IndexPin`, `CheckoutState`, the
425
+ checkout inspection, and the `project_revision_unresolved`,
426
+ `project_revision_uncommitted` and `project_revision_unpublished` errors.
427
+ Cloudfall is on PyPI, so a project pins a release and resolves it like any
428
+ other dependency; `resolve_installed_version` replaces
429
+ `resolve_installed_pin`, and the `init` envelope reports `version` rather
430
+ than a `pin` object. To run an unreleased Cloudfall in a project, point the
431
+ dependency at a checkout with uv's own `[tool.uv.sources]`
432
+
433
+ ### Changed
434
+
435
+ - The quickstart and the Render migration guide install Cloudfall from the
436
+ index rather than from git
437
+
438
+ ## [0.2.1] - 2026-09-20
439
+
440
+ ### Fixed
441
+
442
+ - `cloudfall init` works from a PyPI install. An install from the package
443
+ index records no origin to read a commit from, so `init` failed with
444
+ `project_revision_unresolved` on the first command a new user runs, and
445
+ `--rev` only worked around it by writing a project that installed
446
+ Cloudfall from git instead of from the index. A project now pins whatever
447
+ ran `init`: `cloudfall==<version>` with no `[tool.uv.sources]` table for a
448
+ released install, the commit and repository for a git or source install
449
+ - The project README links the secrets guide and the reference examples at
450
+ the release tag when the pin is a released version, rather than at a
451
+ commit the install does not know
452
+
453
+ ### Changed
454
+
455
+ - `InitOptions` and the `cloudfall init` envelope carry one `pin` instead of
456
+ a `revision` and a `source`: `{"kind": "index", "version": ...}` or
457
+ `{"kind": "git", "revision": ..., "source": ...}`. `resolve_installed_pin`
458
+ replaces `resolve_installed_revision`
459
+ - `--rev` documents that it pins a commit from `--source`, which is now the
460
+ explicit alternative to the default rather than the only mechanism
461
+
462
+ ## [0.2.0] - 2026-09-20
463
+
464
+ ### Added
465
+
466
+ - `cloudfall-mcp` exposes `add_ssh_key`, `add_server_type`, and `add_server`
467
+ so an agent can declare the fleet without writing YAML by hand; each
468
+ returns the `cloudfall add` envelope, writes into the project only, and
469
+ re-validates it. `cloudfall init` stays CLI-only because the server runs
470
+ inside an existing project (#3)
471
+ - `cloudfall init` lays out the secrets setup (`secrets/` and a `.sops.yaml`
472
+ template), runs `git init` unless the directory already lies inside a
473
+ repository, links the secrets guide and the reference examples at the
474
+ pinned commit, and takes `--description` for an About section in the
475
+ README and the `description` in `pyproject.toml`
476
+ - `cloudfall init` writes `AGENTS.md`, the operating contract for AI agents
477
+ in the project: canonical invocation, every command sorted by effect with
478
+ the gate each server-changing command demands, the output contract and
479
+ exit codes, what may be read under `tmp/`, the secrets rule, and git
480
+ discipline; plus a one-line `CLAUDE.md` pointing at it. The
481
+ classification comes from the new `cloudfall.commands` catalog, which
482
+ the tests check against both argparse trees (#2)
483
+ - `cloudfall init` from a source checkout refuses to pin `HEAD` when tracked
484
+ files are modified (`project_revision_uncommitted`) or when `HEAD` is on
485
+ no remote branch (`project_revision_unpublished`), so a project never pins
486
+ a commit that is not the running code or that `uv sync` cannot fetch
487
+ - Fleet repositories consume Cloudfall as a package: the wheel bundles the
488
+ v1 schema catalog and the Ansible engine, and every `--schemas` and
489
+ `--engine` default resolves to the bundled copies (or to the source tree
490
+ when run from a checkout), so no submodule or sibling checkout is needed
491
+ - `cloudfall-engine playbook run` executes a bundled playbook by name or a
492
+ fleet playbook by path under the engine's Ansible configuration, with
493
+ `--roles` directories searched before the bundled roles and `--check`,
494
+ `--diff`, `--syntax-check`, `--limit`, `--tags`, and `--extra-vars`
495
+ passed through; `cloudfall-engine playbook list` names the bundled ones
496
+ - Timer-driven restore drill: an optional `backup.restoreCheckOnCalendar`
497
+ schedule installs an audited restore-check service and timer for
498
+ PostgreSQL and Redis, so backup restorability is verified continuously
499
+ (M10; proven live on 2026-09-11 together with alert delivery to an
500
+ external destination)
501
+
502
+ ### Changed
503
+
504
+ - `ansible-core` is a runtime dependency rather than a development one, so
505
+ an installed `cloudfall` package can run its engine
506
+ - **Breaking: common-vocabulary rename across schemas, CLI, and layout.**
507
+ Resource kinds `Project` → `Application` and `HostProfile` → `ServerType`
508
+ (schema files renamed to match); the `Server` spec field `profile` →
509
+ `serverType`; the `Component` spec field `project` → `application`; the
510
+ top-level `state/` directory → `config/` with resource directories
511
+ `projects/` → `applications/` and `host-profiles/` → `server-types/`;
512
+ `cloudfall state validate` → `cloudfall config validate`; importer flag
513
+ `--project` → `--application`; Python API `validate_state` →
514
+ `validate_config`, `ValidatedState` → `ValidatedConfig`,
515
+ `StateValidationError` → `ConfigValidationError`; Taskfile variable
516
+ `STATE_DIR` → `CONFIG_DIR`; inventory payload key `hostProfiles` →
517
+ `serverTypes` and observed-server key `profile` → `serverType`. Existing
518
+ config directories must be migrated by renaming the directories and the
519
+ `kind`/`profile`/`project` fields; no compatibility aliases are provided
520
+ - `cloudfall-mcp --help` now documents every option and the confirmation
521
+ handshake
522
+ - README quickstart covers cloning, `uv sync`, and automatic Python 3.14
523
+ provisioning; the Task prerequisite is documented with a direct `uv run`
524
+ equivalent
525
+ - Roadmap gained a forward-looking M6 section (catalog breadth, Render API
526
+ import, cutover generator, secrets v2, bare-metal provisioning, backup and
527
+ restore operations, fleet observability)
528
+ - `cloudfall`, `cloudfall-engine`, and `cloudfall-mcp` match long options
529
+ exactly and report usage errors as the JSON error envelope
530
+ (`invalid_argument`, exit 2) instead of argparse prose; unrecognized
531
+ options are named without their values
532
+ - **Breaking: `cloudfall deploy`, `rollback`, `restart`, and `data migrate`
533
+ change servers only with `--yes`.** Without it they run every
534
+ controller-side check (declared component or service, verified artifact,
535
+ declared database, source URL file) and print a `status: plan` preview
536
+ naming the target servers, exit `0`, matching `cloudfall migrate` and the
537
+ MCP confirmation handshake; scripts and Taskfile targets must add `--yes`
538
+ - Runtime path options (`--output`, `--receipts`, `--inventory-file`,
539
+ `--observed`, `--plan-file`, and the other evidence and receipt
540
+ directories) on all three entry points, and the MCP tools' output and plan
541
+ paths, refuse a relative path that climbs out of the project
542
+ (`tmp/../../x`); a location outside the project must be given as an
543
+ absolute path
544
+
545
+ ### Fixed
546
+
547
+ - A guessed `--api-key` or `--source-url` no longer binds to
548
+ `--api-key-file`/`--source-url-file` and echoes the secret in the
549
+ resulting file-not-found error
550
+ - Malformed component, service, proposal, application, server, and release
551
+ ids fail as `invalid_argument` before any work starts instead of crashing
552
+ with a traceback on exit 1
553
+ - JSON documents are flushed as they are written, so `operator run
554
+ --interval` under a pipe or journald delivers each pass instead of
555
+ holding it in the buffer and losing it on termination
556
+ - A missing or unreadable gateway CA, certificate, or key reports
557
+ `operator_gateway_material_invalid` instead of an uncaught traceback
558
+ - `cloudfall init` suggests `uv run cloudfall config validate` as the next
559
+ step; the previous `config validate .` hint no longer parsed
560
+
561
+ ## [0.1.0] - 2026-09-08
562
+
563
+ First public milestone: the Render-to-Hetzner wedge proven live end to end
564
+ on disposable Hetzner Cloud Debian 13 servers (see
565
+ [`docs/proving-runs/`](docs/proving-runs/)).
566
+
567
+ ### Added
568
+
569
+ - Declarative state module: versioned v1 JSON Schemas for `Server`,
570
+ `HostProfile`, `Project`, `Component`, `Service`, `Domain`, `SshPublicKey`,
571
+ and `LoggingStack`, plus observation, receipt, and artifact schemas
572
+ - `cloudfall` CLI and Python API: state validation, typed inventory,
573
+ config-versus-observed drift audit with distinct exit codes, service
574
+ lifecycle status, and the evidence-derived operations dashboard
575
+ (static build and live `dashboard serve`)
576
+ - `cloudfall-engine`: deterministic Ansible inventory rendering and the
577
+ artifact builder (git ref to hashed tarball with release metadata)
578
+ - Engine roles: Debian bootstrap, UTC time baseline, SSH access hardening,
579
+ nftables default-deny firewall, unattended security upgrades, read-only
580
+ inspection, PostgreSQL with peer-auth databases and backup timers,
581
+ Nginx/TLS domain routes with Let's Encrypt issuance, and the guarded
582
+ Loki/Grafana/Alloy logging stack over an mTLS gateway
583
+ - Health-gated deploy slice: digest-verified artifact transfer, symlink
584
+ releases, automatic rollback on failed health checks, and release receipts
585
+ - `cloudfall import render`: `render.yaml` blueprint importer with a
586
+ structured gap report (`IMPORT-REPORT.md`)
587
+ - `cloudfall data migrate`: guided managed-Postgres dump and restore with
588
+ row-count verification and a `DataMigrationReceipt`
589
+ - `cloudfall migrate`: resumable end-to-end orchestrator with persisted step
590
+ progress and a DNS-verification pause at the cutover moment
591
+ - `cloudfall-mcp`: seventeen annotated MCP tools; read-only evidence tools
592
+ exposed freely, every server-changing tool gated behind a two-step
593
+ confirmation handshake