clauz3 0.1.0__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 (231) hide show
  1. clauz3-0.1.0/.github/workflows/docs.yaml +54 -0
  2. clauz3-0.1.0/.github/workflows/main.yaml +55 -0
  3. clauz3-0.1.0/.github/workflows/release.yaml +50 -0
  4. clauz3-0.1.0/.gitignore +10 -0
  5. clauz3-0.1.0/.python-version +1 -0
  6. clauz3-0.1.0/AGENTS.md +51 -0
  7. clauz3-0.1.0/Justfile +32 -0
  8. clauz3-0.1.0/PKG-INFO +260 -0
  9. clauz3-0.1.0/README.md +248 -0
  10. clauz3-0.1.0/docs/examples/bank-all-cases.md +9 -0
  11. clauz3-0.1.0/docs/examples/bank.md +39 -0
  12. clauz3-0.1.0/docs/examples/email-all-cases.md +9 -0
  13. clauz3-0.1.0/docs/examples/email-from-db-all-cases.md +9 -0
  14. clauz3-0.1.0/docs/examples/email-from-db.md +55 -0
  15. clauz3-0.1.0/docs/examples/email.md +47 -0
  16. clauz3-0.1.0/docs/explanation/background.md +193 -0
  17. clauz3-0.1.0/docs/explanation/concepts.md +179 -0
  18. clauz3-0.1.0/docs/explanation/faq.md +83 -0
  19. clauz3-0.1.0/docs/explanation/symbolic-iteration.md +117 -0
  20. clauz3-0.1.0/docs/how-to/approval-service.md +263 -0
  21. clauz3-0.1.0/docs/how-to/install-layers.md +85 -0
  22. clauz3-0.1.0/docs/how-to/integration-testing.md +168 -0
  23. clauz3-0.1.0/docs/how-to/pi-harness.md +315 -0
  24. clauz3-0.1.0/docs/index.md +76 -0
  25. clauz3-0.1.0/docs/macros.py +119 -0
  26. clauz3-0.1.0/docs/quickstart.md +103 -0
  27. clauz3-0.1.0/docs/reference/cli.md +124 -0
  28. clauz3-0.1.0/docs/reference/coverage-policies.md +124 -0
  29. clauz3-0.1.0/docs/reference/effect-specs.md +294 -0
  30. clauz3-0.1.0/docs/reference/python-subset.md +404 -0
  31. clauz3-0.1.0/docs/reference/stdlib.md +103 -0
  32. clauz3-0.1.0/docs/slides/index.html +749 -0
  33. clauz3-0.1.0/docs/superpowers/plans/2026-05-25-symbolic-iteration.md +3666 -0
  34. clauz3-0.1.0/docs/superpowers/specs/2026-05-25-symbolic-iteration-design.md +544 -0
  35. clauz3-0.1.0/docs/todos/effect-ir.md +180 -0
  36. clauz3-0.1.0/docs/todos/ideas.md +119 -0
  37. clauz3-0.1.0/docs/todos/owl-dl-mapping.md +150 -0
  38. clauz3-0.1.0/docs/todos/quantified-aggregates.md +44 -0
  39. clauz3-0.1.0/docs/todos/quantified-shares-value.md +30 -0
  40. clauz3-0.1.0/docs/todos/user-approval-dialog.md +362 -0
  41. clauz3-0.1.0/examples/bank/Justfile +35 -0
  42. clauz3-0.1.0/examples/bank/cases/for_loop_sum_fail.py +11 -0
  43. clauz3-0.1.0/examples/bank/cases/for_loop_sum_pass.py +11 -0
  44. clauz3-0.1.0/examples/bank/cases/max_spend_branch_pass.py +13 -0
  45. clauz3-0.1.0/examples/bank/cases/max_spend_fail.py +11 -0
  46. clauz3-0.1.0/examples/bank/cases/max_spend_pass.py +11 -0
  47. clauz3-0.1.0/examples/bank/cases/negative_amount_fail.py +10 -0
  48. clauz3-0.1.0/examples/bank/cases/only_account_fail.py +10 -0
  49. clauz3-0.1.0/examples/bank/cases/only_account_pass.py +11 -0
  50. clauz3-0.1.0/examples/bank/tools/bank/trusted/contracts.py +17 -0
  51. clauz3-0.1.0/examples/bank/tools/bank/trusted/effects.py +11 -0
  52. clauz3-0.1.0/examples/email/.claude/settings.json +11 -0
  53. clauz3-0.1.0/examples/email/AGENTS.md +92 -0
  54. clauz3-0.1.0/examples/email/CLAUDE.md +1 -0
  55. clauz3-0.1.0/examples/email/Justfile +87 -0
  56. clauz3-0.1.0/examples/email/cases/at_most_two_fail.py +12 -0
  57. clauz3-0.1.0/examples/email/cases/at_most_two_pass.py +11 -0
  58. clauz3-0.1.0/examples/email/cases/bob_at_most_two_branch_pass.py +15 -0
  59. clauz3-0.1.0/examples/email/cases/bob_at_most_two_fail.py +13 -0
  60. clauz3-0.1.0/examples/email/cases/bob_at_most_two_pass.py +12 -0
  61. clauz3-0.1.0/examples/email/cases/content_length_branch_pass.py +13 -0
  62. clauz3-0.1.0/examples/email/cases/content_length_fail.py +10 -0
  63. clauz3-0.1.0/examples/email/cases/content_length_pass.py +11 -0
  64. clauz3-0.1.0/examples/email/cases/dead_branch_pass.py +11 -0
  65. clauz3-0.1.0/examples/email/cases/helper_fail.py +14 -0
  66. clauz3-0.1.0/examples/email/cases/helper_pass.py +14 -0
  67. clauz3-0.1.0/examples/email/cases/no_guarantees_pass.py +12 -0
  68. clauz3-0.1.0/examples/email/cases/nobody_emailed_twice_fail.py +15 -0
  69. clauz3-0.1.0/examples/email/cases/none_fail.py +10 -0
  70. clauz3-0.1.0/examples/email/cases/none_pass.py +9 -0
  71. clauz3-0.1.0/examples/email/cases/only_bob_fail.py +10 -0
  72. clauz3-0.1.0/examples/email/cases/only_bob_pass.py +10 -0
  73. clauz3-0.1.0/examples/email/cases/precondition_fail.py +10 -0
  74. clauz3-0.1.0/examples/email/cases/same_content_branch_pass.py +15 -0
  75. clauz3-0.1.0/examples/email/cases/same_content_fail.py +11 -0
  76. clauz3-0.1.0/examples/email/cases/same_content_pass.py +11 -0
  77. clauz3-0.1.0/examples/email/cases/unique_recipients_branch_pass.py +13 -0
  78. clauz3-0.1.0/examples/email/cases/unique_recipients_fail.py +11 -0
  79. clauz3-0.1.0/examples/email/cases/unique_recipients_pass.py +11 -0
  80. clauz3-0.1.0/examples/email/tools/email/trusted/contracts.py +56 -0
  81. clauz3-0.1.0/examples/email/tools/email/trusted/effects.py +15 -0
  82. clauz3-0.1.0/examples/email/tools/email/trusted/policy.py +7 -0
  83. clauz3-0.1.0/examples/email-from-db/.claude/settings.json +11 -0
  84. clauz3-0.1.0/examples/email-from-db/AGENTS.md +50 -0
  85. clauz3-0.1.0/examples/email-from-db/CLAUDE.md +1 -0
  86. clauz3-0.1.0/examples/email-from-db/Justfile +28 -0
  87. clauz3-0.1.0/examples/email-from-db/cases/all_trivial_loop_pass.py +10 -0
  88. clauz3-0.1.0/examples/email-from-db/cases/count_pass.py +10 -0
  89. clauz3-0.1.0/examples/email-from-db/cases/count_too_tight_fail.py +12 -0
  90. clauz3-0.1.0/examples/email-from-db/cases/email_loop_fail.py +10 -0
  91. clauz3-0.1.0/examples/email-from-db/cases/literal_address_fail.py +10 -0
  92. clauz3-0.1.0/examples/email-from-db/cases/mixed_source_fail.py +12 -0
  93. clauz3-0.1.0/examples/email-from-db/cases/newsletter_pass.py +12 -0
  94. clauz3-0.1.0/examples/email-from-db/cases/no_emails_with_loop_pass.py +9 -0
  95. clauz3-0.1.0/examples/email-from-db/cases/unique_recipients_loop_fail.py +10 -0
  96. clauz3-0.1.0/examples/email-from-db/cases/wrong_column_fail.py +11 -0
  97. clauz3-0.1.0/examples/email-from-db/tools/db/trusted/contracts.py +71 -0
  98. clauz3-0.1.0/examples/email-from-db/tools/db/trusted/effects.py +19 -0
  99. clauz3-0.1.0/examples/email-from-db/tools/db/trusted/schemas.py +8 -0
  100. clauz3-0.1.0/examples/email-from-db/users.csv +5 -0
  101. clauz3-0.1.0/extensions/clauz3/index.ts +319 -0
  102. clauz3-0.1.0/extensions/clauz3/package.json +9 -0
  103. clauz3-0.1.0/mkdocs.yml +113 -0
  104. clauz3-0.1.0/pyproject.toml +64 -0
  105. clauz3-0.1.0/skills/clauz3/SKILL.md +32 -0
  106. clauz3-0.1.0/src/clauz3/__init__.py +42 -0
  107. clauz3-0.1.0/src/clauz3/_vendor/__init__.py +1 -0
  108. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/LICENSE +19 -0
  109. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/__init__.py +20 -0
  110. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_annotations.py +144 -0
  111. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_ast.py +46 -0
  112. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_cached_property.py +28 -0
  113. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/__init__.py +14 -0
  114. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/_context.py +118 -0
  115. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/_layer.py +69 -0
  116. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/_quantifier.py +23 -0
  117. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/_scope.py +43 -0
  118. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_context/_trace.py +30 -0
  119. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_eval_contracts.py +105 -0
  120. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_eval_expr.py +420 -0
  121. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_eval_stmt.py +340 -0
  122. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_exceptions.py +10 -0
  123. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/__init__.py +7 -0
  124. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_builtins.py +173 -0
  125. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_math.py +120 -0
  126. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_os_path.py +29 -0
  127. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_random.py +49 -0
  128. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_re.py +38 -0
  129. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_funcs/_registry.py +23 -0
  130. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_goal.py +18 -0
  131. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_model.py +61 -0
  132. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/__init__.py +49 -0
  133. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_bool.py +130 -0
  134. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_dict.py +257 -0
  135. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_float.py +361 -0
  136. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_func.py +303 -0
  137. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_funcs.py +53 -0
  138. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_int.py +269 -0
  139. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_lambda.py +32 -0
  140. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_list.py +148 -0
  141. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_method.py +58 -0
  142. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_methods.py +57 -0
  143. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_pattern.py +140 -0
  144. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_proxy.py +346 -0
  145. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_registry.py +54 -0
  146. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_row.py +188 -0
  147. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_set.py +291 -0
  148. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_str.py +222 -0
  149. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_type_factory.py +36 -0
  150. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_proxies/_var_tuple.py +250 -0
  151. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_registry.py +37 -0
  152. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_theorem.py +290 -0
  153. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/_types.py +6 -0
  154. clauz3-0.1.0/src/clauz3/_vendor/deal_solver/py.typed +0 -0
  155. clauz3-0.1.0/src/clauz3/approval.py +261 -0
  156. clauz3-0.1.0/src/clauz3/approval_service.py +550 -0
  157. clauz3-0.1.0/src/clauz3/cli.py +387 -0
  158. clauz3-0.1.0/src/clauz3/install.py +314 -0
  159. clauz3-0.1.0/src/clauz3/policy.py +210 -0
  160. clauz3-0.1.0/src/clauz3/prove.py +13 -0
  161. clauz3-0.1.0/src/clauz3/prover.py +287 -0
  162. clauz3-0.1.0/src/clauz3/row.py +142 -0
  163. clauz3-0.1.0/src/clauz3/runner.py +362 -0
  164. clauz3-0.1.0/src/clauz3/spec.py +788 -0
  165. clauz3-0.1.0/src/clauz3/stdlib/README.md +48 -0
  166. clauz3-0.1.0/src/clauz3/stdlib/filesystem/README.md +75 -0
  167. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/Justfile +47 -0
  168. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/never_read_under_fail.py +10 -0
  169. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/never_read_under_pass.py +10 -0
  170. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/never_write_under_fail.py +10 -0
  171. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/never_write_under_pass.py +10 -0
  172. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/only_read_under_pass.py +11 -0
  173. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/only_write_under_branch_pass.py +13 -0
  174. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/only_write_under_fail.py +10 -0
  175. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/only_write_under_pass.py +10 -0
  176. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/read_only_fail.py +11 -0
  177. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/read_only_pass.py +11 -0
  178. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/writes_at_most_fail.py +11 -0
  179. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tests/cases/writes_at_most_pass.py +11 -0
  180. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tools/filesystem/trusted/contracts.py +61 -0
  181. clauz3-0.1.0/src/clauz3/stdlib/filesystem/tools/filesystem/trusted/effects.py +31 -0
  182. clauz3-0.1.0/src/clauz3/stdlib/grep/README.md +73 -0
  183. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/Justfile +36 -0
  184. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/never_read_under_fail.py +10 -0
  185. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/never_read_under_pass.py +10 -0
  186. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/only_pattern_fail.py +10 -0
  187. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/only_pattern_pass.py +11 -0
  188. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/only_read_under_fail.py +10 -0
  189. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/only_read_under_pass.py +10 -0
  190. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/searches_at_most_fail.py +11 -0
  191. clauz3-0.1.0/src/clauz3/stdlib/grep/tests/cases/searches_at_most_pass.py +11 -0
  192. clauz3-0.1.0/src/clauz3/stdlib/grep/tools/grep/trusted/contracts.py +46 -0
  193. clauz3-0.1.0/src/clauz3/stdlib/grep/tools/grep/trusted/effects.py +22 -0
  194. clauz3-0.1.0/tests/conftest.py +13 -0
  195. clauz3-0.1.0/tests/fixtures/loop_email/__init__.py +0 -0
  196. clauz3-0.1.0/tests/fixtures/loop_email/cases/addresses_from_literal_fail.py +10 -0
  197. clauz3-0.1.0/tests/fixtures/loop_email/cases/addresses_from_mixed_source_fail.py +12 -0
  198. clauz3-0.1.0/tests/fixtures/loop_email/cases/addresses_from_users_email_pass.py +11 -0
  199. clauz3-0.1.0/tests/fixtures/loop_email/cases/addresses_from_wrong_column_fail.py +11 -0
  200. clauz3-0.1.0/tests/fixtures/loop_email/cases/all_trivial_loop_pass.py +11 -0
  201. clauz3-0.1.0/tests/fixtures/loop_email/cases/count_too_tight_fail.py +13 -0
  202. clauz3-0.1.0/tests/fixtures/loop_email/cases/count_under_branch_fail.py +11 -0
  203. clauz3-0.1.0/tests/fixtures/loop_email/cases/count_under_pass.py +11 -0
  204. clauz3-0.1.0/tests/fixtures/loop_email/cases/distinct_over_loop_fail.py +11 -0
  205. clauz3-0.1.0/tests/fixtures/loop_email/cases/empty_loop_fail.py +11 -0
  206. clauz3-0.1.0/tests/fixtures/loop_email/cases/empty_no_send_pass.py +10 -0
  207. clauz3-0.1.0/tests/fixtures/loop_email/cases/precondition_over_loop_fail.py +9 -0
  208. clauz3-0.1.0/tests/fixtures/loop_email/cases/shares_value_over_loop_fail.py +10 -0
  209. clauz3-0.1.0/tests/fixtures/loop_email/cases/sum_selector_depends_on_row_fail.py +11 -0
  210. clauz3-0.1.0/tests/fixtures/loop_email/qtrusted/__init__.py +0 -0
  211. clauz3-0.1.0/tests/fixtures/loop_email/qtrusted/contracts.py +51 -0
  212. clauz3-0.1.0/tests/fixtures/loop_email/qtrusted/effects.py +45 -0
  213. clauz3-0.1.0/tests/fixtures/loop_email/qtrusted/rows.py +11 -0
  214. clauz3-0.1.0/tests/test_approval_service.py +288 -0
  215. clauz3-0.1.0/tests/test_bank_prover.py +40 -0
  216. clauz3-0.1.0/tests/test_cli.py +123 -0
  217. clauz3-0.1.0/tests/test_column_ref.py +83 -0
  218. clauz3-0.1.0/tests/test_coverage.py +103 -0
  219. clauz3-0.1.0/tests/test_email_prover.py +64 -0
  220. clauz3-0.1.0/tests/test_for_loop.py +268 -0
  221. clauz3-0.1.0/tests/test_import.py +5 -0
  222. clauz3-0.1.0/tests/test_install.py +116 -0
  223. clauz3-0.1.0/tests/test_model.py +75 -0
  224. clauz3-0.1.0/tests/test_quantified_facts.py +100 -0
  225. clauz3-0.1.0/tests/test_quantified_relations.py +119 -0
  226. clauz3-0.1.0/tests/test_relation_lambdas.py +51 -0
  227. clauz3-0.1.0/tests/test_required.py +101 -0
  228. clauz3-0.1.0/tests/test_row_proxy.py +216 -0
  229. clauz3-0.1.0/tests/test_run.py +231 -0
  230. clauz3-0.1.0/tests/test_stdlib.py +114 -0
  231. clauz3-0.1.0/uv.lock +1180 -0
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: Deploy docs
3
+
4
+ on: # yamllint disable-line rule:truthy
5
+ push:
6
+ branches: [main]
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ pages: write
12
+ id-token: write
13
+
14
+ concurrency:
15
+ group: pages
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Check out repository
23
+ uses: actions/checkout@v4.2.2
24
+ with:
25
+ persist-credentials: false
26
+
27
+ - name: Install uv
28
+ uses: astral-sh/setup-uv@v6.4.3
29
+ with:
30
+ python-version: "3.12"
31
+ enable-cache: true
32
+ cache-dependency-glob: "uv.lock"
33
+
34
+ - name: Install docs dependencies
35
+ run: uv sync --group docs
36
+
37
+ - name: Build site
38
+ run: uv run --group docs mkdocs build --strict
39
+
40
+ - name: Upload Pages artifact
41
+ uses: actions/upload-pages-artifact@v3
42
+ with:
43
+ path: site
44
+
45
+ deploy:
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ environment:
49
+ name: github-pages
50
+ url: ${{ steps.deployment.outputs.page_url }}
51
+ steps:
52
+ - name: Deploy to GitHub Pages
53
+ id: deployment
54
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,55 @@
1
+ # Built from:
2
+ # https://docs.github.com/en/actions/guides/building-and-testing-python
3
+ ---
4
+ name: Build and test
5
+
6
+ on: # yamllint disable-line rule:truthy
7
+ push:
8
+ branches: [main]
9
+ pull_request:
10
+
11
+ env:
12
+ FORCE_COLOR: "1" # Make tools pretty.
13
+
14
+ #permissions: {}
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ python-version: ["3.11", "3.14"]
23
+ fail-fast: false
24
+
25
+ steps:
26
+
27
+ # https://github.com/actions/checkout
28
+ - name: Check out repository
29
+ uses: actions/checkout@v4.2.2
30
+ with:
31
+ persist-credentials: false
32
+
33
+ # https://github.com/astral-sh/setup-uv
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v6.4.3
36
+ with:
37
+ python-version: ${{ matrix.python-version }}
38
+ enable-cache: true
39
+ cache-dependency-glob: "uv.lock"
40
+
41
+ # https://github.com/actions/setup-python
42
+ - name: Set up Python ${{ matrix.python-version }}
43
+ uses: actions/setup-python@v5.6.0
44
+ with:
45
+ python-version: ${{ matrix.python-version }}
46
+
47
+ - name: Install just
48
+ run: |
49
+ uv tool install rust-just
50
+
51
+ - name: Install project
52
+ run: uv sync --dev
53
+
54
+ - name: Run test suite
55
+ run: just test
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: Release to PyPI
3
+
4
+ on: # yamllint disable-line rule:truthy
5
+ push:
6
+ tags: ['v*']
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Check out repository
14
+ uses: actions/checkout@v4.2.2
15
+ with:
16
+ persist-credentials: false
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v6.4.3
20
+ with:
21
+ python-version: "3.12"
22
+ enable-cache: true
23
+ cache-dependency-glob: "uv.lock"
24
+
25
+ - name: Build distributions
26
+ run: uv build
27
+
28
+ - name: Upload dist artifacts
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ needs: build
36
+ runs-on: ubuntu-latest
37
+ environment:
38
+ name: pypi
39
+ url: https://pypi.org/p/clauz3
40
+ permissions:
41
+ id-token: write
42
+ steps:
43
+ - name: Download dist artifacts
44
+ uses: actions/download-artifact@v4
45
+ with:
46
+ name: dist
47
+ path: dist/
48
+
49
+ - name: Publish to PyPI (trusted publishing)
50
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ .mypy_cache/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ .venv/
5
+ __pycache__/
6
+ build/
7
+ dist/
8
+ *.egg-info/
9
+ *.py[cod]
10
+ site/
@@ -0,0 +1 @@
1
+ 3.11
clauz3-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,51 @@
1
+ # Repository Guidelines
2
+
3
+ ## Project Structure & Module Organization
4
+
5
+ `src/clauz3/` contains the CLI, prover, contract-spec DSL, and vendored
6
+ `deal_solver` code under `_vendor/`. Tests live in `tests/`. Worked examples
7
+ live in `examples/<domain>/`: each has `cases/` for agent-authored proof
8
+ fixtures and `tools/<domain>/trusted/` for trusted effects and contracts.
9
+ Design notes are in `docs/` and `docs/todos/`. CI is defined in
10
+ `.github/workflows/main.yaml`.
11
+
12
+ ## Build, Test, and Development Commands
13
+
14
+ - `uv sync --dev`: install the project and development tools.
15
+ - `just test`: run pytest, ruff, mypy, and every example Justfile.
16
+ - `just check`: run ruff formatting/linting and strict mypy.
17
+ - `just pytest`: run only the pytest suite.
18
+ - `just examples`: run example proofs only.
19
+ - `uv run clauz3 prove --trusted-root tools/email/trusted cases/only_bob_pass.py`
20
+ from an example directory: prove one entry file against a trusted root.
21
+
22
+ ## Coding Style & Naming Conventions
23
+
24
+ Use Python 3.11+ syntax and 4-space indentation. Ruff enforces linting,
25
+ formatting, import order, and selected modernization rules. Mypy runs in strict
26
+ mode for `src`, `tests`, and `examples`; `_vendor` is excluded for now. Keep
27
+ public package imports as `clauz3`, not the repository name. Trusted example
28
+ modules should use `tools/<domain>/trusted/{effects.py,contracts.py}`.
29
+
30
+ ## Testing Guidelines
31
+
32
+ Tests use pytest and are named `tests/test_*.py`. Add focused pytest cases for
33
+ core behavior and example case files for proof behavior. Cases under
34
+ `examples/*/cases/` should be valid Python and remain mypy-clean. Example
35
+ Justfiles should include both expected-pass and expected-fail proof commands.
36
+ Run `just test` before pushing.
37
+
38
+ ## Commit & Pull Request Guidelines
39
+
40
+ Recent commits use short imperative subjects, for example `Add CI and scalable
41
+ mypy roots` or `Rename package to clauz3`. Keep commits scoped. PRs should
42
+ include a concise summary, user-visible impact, and validation results such as
43
+ `just test`. Link issues when applicable. Screenshots are not needed unless a
44
+ future change adds UI.
45
+
46
+ ## Trust Boundary Notes
47
+
48
+ Only code under trusted roots should define `@deal.has(...)` effects or
49
+ `@contract` helpers. Agent-authored entry files should import trusted tools and
50
+ state guarantees with `@clauz3.guarantee(...)`. Do not make untrusted code a
51
+ trusted boundary just to satisfy a proof.
clauz3-0.1.0/Justfile ADDED
@@ -0,0 +1,32 @@
1
+ set shell := ["bash", "-cu"]
2
+
3
+ example-justfiles := `find examples -mindepth 2 -maxdepth 2 -name Justfile | sort | paste -sd' ' -`
4
+ example-roots := `find examples -mindepth 1 -maxdepth 1 -type d | sort | paste -sd: -`
5
+ fixture-roots := `find tests/fixtures -mindepth 1 -maxdepth 1 -type d | sort | paste -sd: -`
6
+ stdlib-justfiles := `find src/clauz3/stdlib -name Justfile | sort | paste -sd' ' -`
7
+
8
+ test: pytest check examples stdlib
9
+
10
+ pytest:
11
+ uv run pytest
12
+
13
+ check:
14
+ uv run ruff check .
15
+ uv run ruff format --check .
16
+ MYPYPATH="src:{{example-roots}}:{{fixture-roots}}" uv run mypy src tests examples
17
+
18
+ examples:
19
+ for justfile in {{example-justfiles}}; do \
20
+ just -f "$justfile" test; \
21
+ done
22
+
23
+ stdlib:
24
+ for justfile in {{stdlib-justfiles}}; do \
25
+ just -f "$justfile" test; \
26
+ done
27
+
28
+ docs:
29
+ uv run --group docs mkdocs build --strict
30
+
31
+ serve-docs port="8077":
32
+ uv run --group docs mkdocs serve --dev-addr 127.0.0.1:{{port}}
clauz3-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,260 @@
1
+ Metadata-Version: 2.4
2
+ Name: clauz3
3
+ Version: 0.1.0
4
+ Summary: Static contract experiments for agent-authored Python
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: astroid>=4.1.2
7
+ Requires-Dist: deal>=4.24.6
8
+ Requires-Dist: fastapi>=0.136.3
9
+ Requires-Dist: uvicorn>=0.48.0
10
+ Requires-Dist: z3-solver>=4.16.0.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # clauz3
14
+
15
+ Static contracts for agent-authored Python.
16
+
17
+ `clauz3` experiments with a different permission surface for agents: the
18
+ agent writes a small Python program, attaches a contract describing its trusted
19
+ side effects, and a static prover checks the contract before anything runs.
20
+
21
+ The contract is the thing the user should be asked to accept. The program is
22
+ still available for inspection, but a user should not have to read every branch
23
+ to answer questions like:
24
+
25
+ - Will this email anyone other than Bob?
26
+ - Will this email the same person twice?
27
+ - Will this withdraw more than $5 total?
28
+ - Will this write outside a sandbox?
29
+
30
+ ## Current Shape
31
+
32
+ Projects are split into three layers:
33
+
34
+ - trusted roots such as `tools/email/trusted/`: small audited modules
35
+ containing both side-effecting functions and reusable domain contracts. These
36
+ are the environment contract: the prover trusts their signatures,
37
+ `@deal.has(...)` markers, `@deal.pre(...)` preconditions, and `@contract`
38
+ helper definitions.
39
+ - agent-authored code: ordinary Python that imports trusted functions and adds
40
+ `@clauz3.guarantee(...)` decorators to the function being proved.
41
+
42
+ Trusted calls bottom out into symbolic effect facts. For example:
43
+
44
+ ```python
45
+ @deal.pre(lambda addr, msg: "@" in addr)
46
+ @deal.has("trusted")
47
+ def send_email(addr: str, msg: str) -> None:
48
+ ...
49
+ ```
50
+
51
+ A call to `send_email("bob@example.com", "hi")` creates a fact whose relation
52
+ name is `send_email`, whose fields are `addr` and `msg`, and whose condition is
53
+ the branch condition under which the call is reachable.
54
+
55
+ Domain logic can query those generated facts:
56
+
57
+ ```python
58
+ from clauz3.spec import ContractSpec, contract, effect
59
+ from clauz3.spec import no_guarantees as core_no_guarantees
60
+
61
+
62
+ Email = effect("send_email")
63
+
64
+
65
+ @contract
66
+ def only(addresses: list[str]) -> ContractSpec:
67
+ return Email.all(lambda e: e.addr in addresses)
68
+
69
+
70
+ @contract
71
+ def no_guarantees() -> ContractSpec:
72
+ return core_no_guarantees()
73
+
74
+
75
+ @contract
76
+ def unique_recipients() -> ContractSpec:
77
+ return Email.distinct(lambda e: e.addr)
78
+ ```
79
+
80
+ Agent code then states guarantees in terms of that vocabulary:
81
+
82
+ ```python
83
+ import clauz3
84
+ from tools.email.trusted import contracts as emails
85
+ from tools.email.trusted.effects import send_email
86
+
87
+
88
+ @clauz3.guarantee(emails.only(["bob@example.com"]))
89
+ @clauz3.guarantee(emails.unique_recipients())
90
+ def main() -> None:
91
+ send_email("bob@example.com", "hi")
92
+ ```
93
+
94
+ Run the prover:
95
+
96
+ ```bash
97
+ cd examples/email
98
+ uv run clauz3 prove \
99
+ --trusted-root tools/email/trusted \
100
+ cases/only_bob_pass.py
101
+ ```
102
+
103
+ For programs that combine domains, either repeat `--trusted-root` or pass
104
+ several roots with `--trusted-roots`:
105
+
106
+ ```bash
107
+ uv run clauz3 prove plan.py \
108
+ --trusted-roots tools/email/trusted tools/db/trusted
109
+ ```
110
+
111
+ ## Installing a trusted layer
112
+
113
+ To start a new project from an existing one, copy its trusted `tools/` layer
114
+ with `clauz3 install`. It takes a local path to a project (or directly to a
115
+ `tools/` folder) and copies each `tools/<domain>/` into the current directory:
116
+
117
+ ```bash
118
+ clauz3 install /path/to/repo/examples/email
119
+ ```
120
+
121
+ Pass `--into <dir>` to target a different destination, `--force` to overwrite an
122
+ existing layer, and `--skills` to also generate
123
+ `agents/skills/<domain>/SKILL.md` describing the installed effects and
124
+ contracts:
125
+
126
+ ```bash
127
+ clauz3 install /path/to/repo/examples/email --into my-project --skills
128
+ ```
129
+
130
+ For now this is a trivial filesystem copy. Future work will add signing so a
131
+ user has guarantees that the installed layer is untouched.
132
+
133
+ ## Standard library tools
134
+
135
+ `clauz3` ships a small set of **real (non-mock) tools** under
136
+ [`src/clauz3/stdlib`](src/clauz3/stdlib). After `pip install clauz3`, install
137
+ one into the current repo with the `stdlib:` scheme:
138
+
139
+ ```bash
140
+ clauz3 install stdlib:filesystem
141
+ clauz3 install stdlib:grep
142
+ ```
143
+
144
+ The bundled tools are located with `importlib.resources`, so this works the
145
+ same from a source checkout or an installed wheel. (A bare `clauz3 install
146
+ filesystem` also works as a shorthand.)
147
+
148
+ | Tool | Effects | Headline contracts |
149
+ | --- | --- | --- |
150
+ | [`filesystem`](src/clauz3/stdlib/filesystem) | `read_file`, `write_file` | `read_only`, `only_write_under`, `never_read_under`, `writes_at_most` |
151
+ | [`grep`](src/clauz3/stdlib/grep) | `grep` (imports `filesystem`) | `only_read_under`, `never_read_under`, `searches_at_most`, `only_pattern` |
152
+
153
+ These differ from the `examples/` layers in two ways: their effect bodies do
154
+ real work (so the prover skips them but `clauz3 run` executes them), and their
155
+ proof cases live under `tests/cases/` with a `tests/Justfile` rather than at the
156
+ top level. `just stdlib` proves them all.
157
+
158
+ ## Examples
159
+
160
+ The repo currently has three worked examples:
161
+
162
+ | Example | Contracts | What it demonstrates |
163
+ | --- | --- | --- |
164
+ | [`examples/email`](examples/email) | `emails.only`, `emails.none`, `emails.no_guarantees`, `emails.unique_recipients`, `emails.content_length_at_most` | allowlists, explicit absence of guarantees, absence of effects, pairwise uniqueness, string length bounds |
165
+ | [`examples/bank`](examples/bank) | `bank.max_spend`, `bank.only_account` | numeric aggregation, field equality, and fixed-bound loop unrolling over trusted calls |
166
+ | [`examples/email-from-db`](examples/email-from-db) | `emails.addresses_from`, `db.only_table`, `db.only_where` | for-loops over trusted query returns, column-binding constraints |
167
+
168
+ All examples use generic effect relations inferred from trusted function
169
+ signatures. There is no email-specific, bank-specific, or database-specific logic in the core.
170
+
171
+ ## Status
172
+
173
+ This is experimental. The current prover vendors and extends `deal-solver`.
174
+ Implemented pieces include:
175
+
176
+ - `clauz3.guarantee(...)`, a no-op runtime decorator consumed by the prover.
177
+ - trusted-call fact recording at `@deal.has(...)` boundaries.
178
+ - `clauz3.spec.effect(...)` for relation-style queries over trusted facts.
179
+ - relation primitives: `no_guarantees`, `all`, `empty`, `where`, `count`,
180
+ `distinct`, numeric `sum`, and `shares_value`.
181
+ - `clauz3 prove` for proving examples from the CLI.
182
+ - `clauz3 install` for copying a trusted `tools/` layer from a local path or a
183
+ bundled stdlib tool (`stdlib:filesystem`, `stdlib:grep`), optionally
184
+ generating `agents/skills/<domain>/SKILL.md` stubs.
185
+ - `clauz3 run` for proving a complete inline program, submitting an approval
186
+ request to an externally configured approval service, and executing `main`
187
+ only after an approval receipt is returned.
188
+ - `clauz3 approval-service` for starting a simple localhost FastAPI approval
189
+ service with REST endpoints and a browser UI for user decisions.
190
+ - `clauz3 mock-approval-service` for config-driven tests and local demos.
191
+ - For-loops over `list[Row]`-returning trusted calls, with column-binding
192
+ contracts via `UserRow.email` markers. See
193
+ [docs/explanation/symbolic-iteration.md](docs/explanation/symbolic-iteration.md).
194
+ - Domain coverage policies declared in a trusted-root `policy.py`: contracts a
195
+ domain `recommended`s are flagged in the approval UI when a used domain is
196
+ under-constrained, and contracts it marks `required` are conjoined into the
197
+ proof and rejected if unmet. See
198
+ [docs/reference/coverage-policies.md](docs/reference/coverage-policies.md).
199
+
200
+ Important limitations:
201
+
202
+ - Contract helper functions are still executed as builder functions. Their
203
+ lambda bodies are parsed as a small expression subset, but the helper body
204
+ itself is not yet fully AST-validated.
205
+ - Runtime receipt enforcement in trusted functions is not implemented yet; the
206
+ first `run` slice requires a receipt before execution and exposes it to the
207
+ process as `CLAUZ3_APPROVAL_RECEIPT`.
208
+ - `clauz3 run` includes source checks and reduced builtins, but it is not a
209
+ hardened Python or OS sandbox. The agent harness must still restrict execution
210
+ to the `clauz3` command path.
211
+ - The relation language is intentionally small. Richer string predicates,
212
+ richer aggregates, Datalog-style rules, and deterministic
213
+ natural language summaries are future work.
214
+
215
+ More detail:
216
+
217
+ - [FAQ and terminology](docs/explanation/faq.md)
218
+ - [Concepts](docs/explanation/concepts.md)
219
+ - [Background and related work](docs/explanation/background.md)
220
+ - [Effect specs](docs/reference/effect-specs.md)
221
+ - [Coverage policies](docs/reference/coverage-policies.md)
222
+ - [Symbolic iteration](docs/explanation/symbolic-iteration.md)
223
+ - [ClauZ3 Python subset](docs/reference/python-subset.md)
224
+ - [Approval service](docs/how-to/approval-service.md)
225
+ - [User approval dialog design](docs/todos/user-approval-dialog.md)
226
+ - [Ideas](docs/todos/ideas.md)
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ uv sync --dev
232
+ just test
233
+ ```
234
+
235
+ ## Built on
236
+
237
+ <p>
238
+ <a href="https://deal.readthedocs.io/">
239
+ <img src="https://raw.githubusercontent.com/life4/deal/master/logo.png"
240
+ alt="deal logo" height="48" align="middle">
241
+ </a>
242
+ &nbsp;&nbsp;
243
+ <a href="https://github.com/Z3Prover/z3">
244
+ <img src="https://github.com/Z3Prover.png" alt="Z3 logo" height="48"
245
+ align="middle" style="border-radius: 8px;">
246
+ </a>
247
+ </p>
248
+
249
+ ClauZ3 layers on two upstream projects and would not exist without them:
250
+
251
+ - **[deal](https://deal.readthedocs.io/)** — the Python runtime contract
252
+ engine whose `@deal.pre`, `@deal.post`, and `@deal.has` decorators define
253
+ the trusted-layer vocabulary. ClauZ3 reads the same decorators and
254
+ discharges them statically; in runtime-only mode, deal enforces them at
255
+ execution. See
256
+ [Static proof vs runtime](docs/explanation/concepts.md#static-proof-vs-runtime)
257
+ for how the two layers relate.
258
+ - **[Z3](https://github.com/Z3Prover/z3)** — Microsoft Research's SMT solver,
259
+ the back-end the prover compiles every guarantee into. The vendored
260
+ `deal-solver` machinery is what bridges Python AST and Z3 constraints.