synalog 2.0.0__tar.gz → 2.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 (184) hide show
  1. {synalog-2.0.0 → synalog-2.1.0}/Cargo.lock +1 -1
  2. {synalog-2.0.0 → synalog-2.1.0}/Cargo.toml +1 -1
  3. {synalog-2.0.0 → synalog-2.1.0}/PKG-INFO +10 -10
  4. {synalog-2.0.0 → synalog-2.1.0}/README.md +9 -9
  5. {synalog-2.0.0 → synalog-2.1.0}/docs/assertions.md +1 -1
  6. {synalog-2.0.0 → synalog-2.1.0}/docs/cli.md +23 -9
  7. {synalog-2.0.0 → synalog-2.1.0}/docs/development.md +1 -1
  8. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.log +2092 -2327
  9. synalog-2.1.0/docs/examples/functions.log +103 -0
  10. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/recursion.log +20 -20
  11. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/run.py +2 -2
  12. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/syntax.log +7 -7
  13. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal.log +3 -3
  14. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal_graph.log +11 -11
  15. {synalog-2.0.0 → synalog-2.1.0}/docs/getting-started.md +1 -1
  16. {synalog-2.0.0 → synalog-2.1.0}/docs/language/index.md +4 -4
  17. {synalog-2.0.0 → synalog-2.1.0}/docs/python-api.md +4 -4
  18. {synalog-2.0.0 → synalog-2.1.0}/pyproject.toml +1 -1
  19. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/checking.py +37 -36
  20. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/cli.py +83 -114
  21. synalog-2.1.0/python/synalog/config.py +54 -0
  22. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/execution.py +11 -30
  23. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/introspect.py +23 -33
  24. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/project.py +32 -49
  25. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/runners.py +82 -91
  26. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/SKILL.md +10 -10
  27. synalog-2.0.0/docs/examples/functions.log +0 -103
  28. synalog-2.0.0/python/synalog/config.py +0 -113
  29. {synalog-2.0.0 → synalog-2.1.0}/.gitignore +0 -0
  30. {synalog-2.0.0 → synalog-2.1.0}/LICENSE +0 -0
  31. {synalog-2.0.0 → synalog-2.1.0}/NOTICE +0 -0
  32. {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog-dark.svg +0 -0
  33. {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog-light.svg +0 -0
  34. {synalog-2.0.0 → synalog-2.1.0}/docs/assets/synalog_favicon.png +0 -0
  35. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/results.json +0 -0
  36. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/speedup_by_engine.png +0 -0
  37. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/speedup_distribution.png +0 -0
  38. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/summary.md +0 -0
  39. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/summary.png +0 -0
  40. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/time_comparison.png +0 -0
  41. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark/verify_by_engine.png +0 -0
  42. {synalog-2.0.0 → synalog-2.1.0}/docs/benchmark.md +0 -0
  43. {synalog-2.0.0 → synalog-2.1.0}/docs/differences-datalog.md +0 -0
  44. {synalog-2.0.0 → synalog-2.1.0}/docs/differences.md +0 -0
  45. {synalog-2.0.0 → synalog-2.1.0}/docs/engines.md +0 -0
  46. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/aggregation.l +0 -0
  47. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/aggregation.log +0 -0
  48. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.l +0 -0
  49. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/axioms.ttl +0 -0
  50. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/directives.l +0 -0
  51. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/directives.log +0 -0
  52. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functions.l +0 -0
  53. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functors.l +0 -0
  54. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/functors.log +0 -0
  55. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/getting_started.l +0 -0
  56. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/getting_started.log +0 -0
  57. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/knowledge_graphs.l +0 -0
  58. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/knowledge_graphs.log +0 -0
  59. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/recursion.l +0 -0
  60. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/concepts/Customer.l +0 -0
  61. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/concepts/OrderStatus.l +0 -0
  62. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/data/orders.csv +0 -0
  63. /synalog-2.0.0/docs/examples/shop/synalog.toml → /synalog-2.1.0/docs/examples/shop/layer.toml +0 -0
  64. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/CustomerRevenue.l +0 -0
  65. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/OrdersByStatus.l +0 -0
  66. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/rules/TopCustomers.l +0 -0
  67. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop/tables/Orders.l +0 -0
  68. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/shop.log +0 -0
  69. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/concepts/Device.l +0 -0
  70. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/concepts/TestStatus.l +0 -0
  71. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/data/smoke_tests.csv +0 -0
  72. /synalog-2.0.0/docs/examples/smoke/synalog.toml → /synalog-2.1.0/docs/examples/smoke/layer.toml +0 -0
  73. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/rules/FailuresByDevice.l +0 -0
  74. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/rules/RunsPerDay.l +0 -0
  75. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke/tables/SmokeTests.l +0 -0
  76. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/smoke.log +0 -0
  77. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/syntax.l +0 -0
  78. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal.l +0 -0
  79. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/temporal_graph.l +0 -0
  80. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/verification.l +0 -0
  81. {synalog-2.0.0 → synalog-2.1.0}/docs/examples/verification.log +0 -0
  82. {synalog-2.0.0 → synalog-2.1.0}/docs/index.md +0 -0
  83. {synalog-2.0.0 → synalog-2.1.0}/docs/javascripts/playground.js +0 -0
  84. {synalog-2.0.0 → synalog-2.1.0}/docs/javascripts/summarize.js +0 -0
  85. {synalog-2.0.0 → synalog-2.1.0}/docs/knowledge-graphs.md +0 -0
  86. {synalog-2.0.0 → synalog-2.1.0}/docs/language/aggregation.md +0 -0
  87. {synalog-2.0.0 → synalog-2.1.0}/docs/language/directives.md +0 -0
  88. {synalog-2.0.0 → synalog-2.1.0}/docs/language/functions.md +0 -0
  89. {synalog-2.0.0 → synalog-2.1.0}/docs/language/functors.md +0 -0
  90. {synalog-2.0.0 → synalog-2.1.0}/docs/language/recursion.md +0 -0
  91. {synalog-2.0.0 → synalog-2.1.0}/docs/language/syntax.md +0 -0
  92. {synalog-2.0.0 → synalog-2.1.0}/docs/language/temporal.md +0 -0
  93. {synalog-2.0.0 → synalog-2.1.0}/docs/playground/vendor/codemirror.js +0 -0
  94. {synalog-2.0.0 → synalog-2.1.0}/docs/playground.md +0 -0
  95. {synalog-2.0.0 → synalog-2.1.0}/docs/stylesheets/extra.css +0 -0
  96. {synalog-2.0.0 → synalog-2.1.0}/docs/verification.md +0 -0
  97. {synalog-2.0.0 → synalog-2.1.0}/docs/why.md +0 -0
  98. {synalog-2.0.0 → synalog-2.1.0}/img/synalog-dark.svg +0 -0
  99. {synalog-2.0.0 → synalog-2.1.0}/img/synalog-light.svg +0 -0
  100. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/__init__.py +0 -0
  101. {synalog-2.0.0 → synalog-2.1.0}/python/synalog/__main__.py +0 -0
  102. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/Department.l +0 -0
  103. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/Person.l +0 -0
  104. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/concepts/WorksIn.l +0 -0
  105. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/data/departments.csv +0 -0
  106. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/data/employees.csv +0 -0
  107. /synalog-2.0.0/skills/synalog/examples/company/synalog.toml → /synalog-2.1.0/skills/synalog/examples/company/layer.toml +0 -0
  108. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/rules/TeamMate.l +0 -0
  109. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/tables/Departments.l +0 -0
  110. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/company/tables/Employees.l +0 -0
  111. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/concepts/AllManagers.l +0 -0
  112. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/data/employees.csv +0 -0
  113. /synalog-2.0.0/skills/synalog/examples/org/synalog.toml → /synalog-2.1.0/skills/synalog/examples/org/layer.toml +0 -0
  114. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/org/tables/Employees.l +0 -0
  115. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/concepts/Region.l +0 -0
  116. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/data/sales.csv +0 -0
  117. /synalog-2.0.0/skills/synalog/examples/sales/synalog.toml → /synalog-2.1.0/skills/synalog/examples/sales/layer.toml +0 -0
  118. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/MonthlySales.l +0 -0
  119. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/TopRegion.l +0 -0
  120. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/rules/TotalByRegion.l +0 -0
  121. {synalog-2.0.0 → synalog-2.1.0}/skills/synalog/examples/sales/tables/Sales.l +0 -0
  122. {synalog-2.0.0 → synalog-2.1.0}/src/assertion/mod.rs +0 -0
  123. {synalog-2.0.0 → synalog-2.1.0}/src/assertion/parse.rs +0 -0
  124. {synalog-2.0.0 → synalog-2.1.0}/src/assertion/report.rs +0 -0
  125. {synalog-2.0.0 → synalog-2.1.0}/src/assertion/translate.rs +0 -0
  126. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/annotations.rs +0 -0
  127. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/annotations_test.rs +0 -0
  128. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/concertina.rs +0 -0
  129. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/dialects.rs +0 -0
  130. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/dialects_test.rs +0 -0
  131. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/expr_translate.rs +0 -0
  132. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/expr_translate_test.rs +0 -0
  133. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/functors.rs +0 -0
  134. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/functors_test.rs +0 -0
  135. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/mod.rs +0 -0
  136. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/program.rs +0 -0
  137. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/program_test.rs +0 -0
  138. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/rule_translate.rs +0 -0
  139. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/rule_translate_test.rs +0 -0
  140. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/builder.rs +0 -0
  141. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/built_in.rs +0 -0
  142. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/edge.rs +0 -0
  143. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/expression.rs +0 -0
  144. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/graph.rs +0 -0
  145. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/inference.rs +0 -0
  146. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/intersection.rs +0 -0
  147. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/mod.rs +0 -0
  148. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/type_inference/types.rs +0 -0
  149. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/universe.rs +0 -0
  150. {synalog-2.0.0 → synalog-2.1.0}/src/compiler/universe_test.rs +0 -0
  151. {synalog-2.0.0 → synalog-2.1.0}/src/errors.rs +0 -0
  152. {synalog-2.0.0 → synalog-2.1.0}/src/lib.rs +0 -0
  153. {synalog-2.0.0 → synalog-2.1.0}/src/parser/json.rs +0 -0
  154. {synalog-2.0.0 → synalog-2.1.0}/src/parser/json_test.rs +0 -0
  155. {synalog-2.0.0 → synalog-2.1.0}/src/parser/mod.rs +0 -0
  156. {synalog-2.0.0 → synalog-2.1.0}/src/parser/names.rs +0 -0
  157. {synalog-2.0.0 → synalog-2.1.0}/src/parser/parse.rs +0 -0
  158. {synalog-2.0.0 → synalog-2.1.0}/src/parser/parse_test.rs +0 -0
  159. {synalog-2.0.0 → synalog-2.1.0}/src/parser/rewrite.rs +0 -0
  160. {synalog-2.0.0 → synalog-2.1.0}/src/parser/rewrite_test.rs +0 -0
  161. {synalog-2.0.0 → synalog-2.1.0}/src/parser/span.rs +0 -0
  162. {synalog-2.0.0 → synalog-2.1.0}/src/parser/span_test.rs +0 -0
  163. {synalog-2.0.0 → synalog-2.1.0}/src/parser/traverse.rs +0 -0
  164. {synalog-2.0.0 → synalog-2.1.0}/src/parser/traverse_test.rs +0 -0
  165. {synalog-2.0.0 → synalog-2.1.0}/src/python.rs +0 -0
  166. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/arity.rs +0 -0
  167. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/assertions.rs +0 -0
  168. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/contradiction.rs +0 -0
  169. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/directives.rs +0 -0
  170. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/front_matter.rs +0 -0
  171. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/functors.rs +0 -0
  172. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/mod.rs +0 -0
  173. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/orderby.rs +0 -0
  174. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/positional.rs +0 -0
  175. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/recursion.rs +0 -0
  176. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/reserved.rs +0 -0
  177. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/safety.rs +0 -0
  178. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/sqlexpr.rs +0 -0
  179. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/stratification.rs +0 -0
  180. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/undefined.rs +0 -0
  181. {synalog-2.0.0 → synalog-2.1.0}/src/verifier/vars.rs +0 -0
  182. {synalog-2.0.0 → synalog-2.1.0}/src/wasm.rs +0 -0
  183. {synalog-2.0.0 → synalog-2.1.0}/uv.lock +0 -0
  184. {synalog-2.0.0 → synalog-2.1.0}/zensical.toml +0 -0
@@ -2348,7 +2348,7 @@ dependencies = [
2348
2348
 
2349
2349
  [[package]]
2350
2350
  name = "synalog"
2351
- version = "2.0.0"
2351
+ version = "2.1.0"
2352
2352
  dependencies = [
2353
2353
  "anyhow",
2354
2354
  "duckdb",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "synalog"
3
- version = "2.0.0"
3
+ version = "2.1.0"
4
4
  edition = "2024"
5
5
  description = "Logic programming for AI agents: Datalog-family language compiling to optimized SQL"
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synalog
3
- Version: 2.0.0
3
+ Version: 2.1.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: 3
@@ -162,13 +162,13 @@ $ synalog program.l run EngineeringTeam
162
162
  - `--limit` / `--offset` paginate the result.
163
163
  - `--csv` (with `run`) prints results as CSV instead of the rendered table.
164
164
  - `--search REGEX` (with `print`/`run`) keeps only rows where some column matches the regular expression `REGEX`, e.g. `synalog program.l run Customers --search "(?i)acme"`. In the interactive session the same is `.search Customers (?i)acme`.
165
- - `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (with `--dsn` or `SYNALOG_PSQL_DSN`) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
165
+ - `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (in a [project](#projects-layertoml) connected to it) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
166
166
  - `import path.to.file.Pred;` statements resolve `path/to/file.l` against the program file's directory, then the current directory; pass `--import-root DIR` (repeatable) to search elsewhere.
167
167
  - `--load TABLE=PATH` (repeatable) loads a csv/tsv/json/jsonl/parquet file as a table before running, e.g. `synalog senior.l run Senior --load employees=employees.csv`.
168
168
 
169
- ### Projects: `synalog.toml`
169
+ ### Projects: `layer.toml`
170
170
 
171
- A folder with a `synalog.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
171
+ A folder with a `layer.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
172
172
 
173
173
  ```toml
174
174
  [connection]
@@ -180,7 +180,7 @@ user = "analyst"
180
180
  schema = "public"
181
181
  ```
182
182
 
183
- Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database; `--engine`, an `@Engine` annotation, `--dsn` and `SYNALOG_<ENGINE>_DSN` still take precedence. The fields of every engine are in `synalog.project.ENGINES`.
183
+ Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database. The project's file is the only place a connection comes from; `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. `synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. The fields of every engine are in `synalog.project.ENGINES`.
184
184
 
185
185
  Running `synalog` with no arguments starts an interactive session, in the spirit of `python` (the options above, e.g. `--engine` or `--load`, apply to it too):
186
186
 
@@ -262,7 +262,7 @@ for name, sql in sqls.items():
262
262
  print(name, sql)
263
263
  ```
264
264
 
265
- ### `execute(source, predicate, engine=None, dsn=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
265
+ ### `execute(source, predicate, engine=None, project=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
266
266
 
267
267
  Run a predicate on its database (the program's or project's engine and connection) and return `(columns, rows)`. Each recursion stops as soon as a step changes nothing, so it costs the steps its data needs, and `@Recursive(P, -1)` (until nothing changes) runs on every engine. `plan()` returns the same steps for a host that runs them itself.
268
268
 
@@ -270,9 +270,9 @@ Run a predicate on its database (the program's or project's engine and connectio
270
270
  columns, rows = synalog.execute(source, "TopCustomers", limit=20)
271
271
  ```
272
272
 
273
- ### `check(source, engine=None, import_root=None, assertions=True, dsn=None) -> tuple[list[str], list[str]]`
273
+ ### `check(source, engine=None, import_root=None, assertions=True, project=None) -> tuple[list[str], list[str]]`
274
274
 
275
- Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `synalog.toml` has a `[connection]` (or given a `dsn`), it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
275
+ Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `layer.toml` has a `[connection]` — the current directory's, or the folder `project` names — it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
276
276
 
277
277
  ```python
278
278
  errors, warnings = synalog.check(source)
@@ -285,7 +285,7 @@ All of these functions accept an optional `engine` keyword that overrides the pr
285
285
 
286
286
  ## Language overview
287
287
 
288
- A Synalog program is a **project**: a folder with a `synalog.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
288
+ A Synalog program is a **project**: a folder with a `layer.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
289
289
 
290
290
  ```
291
291
  shop/
@@ -293,7 +293,7 @@ shop/
293
293
  ├── concepts/Customer.l # entities and relationships extracted from tables
294
294
  ├── rules/CustomerRevenue.l # insights derived from concepts
295
295
  ├── rules/TopCustomers.l
296
- └── synalog.toml # the project's name, description and database
296
+ └── layer.toml # the project's name, description and database
297
297
  ```
298
298
 
299
299
  Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
@@ -120,13 +120,13 @@ $ synalog program.l run EngineeringTeam
120
120
  - `--limit` / `--offset` paginate the result.
121
121
  - `--csv` (with `run`) prints results as CSV instead of the rendered table.
122
122
  - `--search REGEX` (with `print`/`run`) keeps only rows where some column matches the regular expression `REGEX`, e.g. `synalog program.l run Customers --search "(?i)acme"`. In the interactive session the same is `.search Customers (?i)acme`.
123
- - `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (with `--dsn` or `SYNALOG_PSQL_DSN`) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
123
+ - `run` executes on `duckdb` (the default), `sqlite` (stdlib) or `psql` (in a [project](#projects-layertoml) connected to it) out of the box: synalog depends on duckdb and on psycopg with its bundled libpq, on macOS and Linux alike. For other engines, install their driver (`pip install 'synalog[trino]'`, ...) or use `print` and run the SQL with your own client.
124
124
  - `import path.to.file.Pred;` statements resolve `path/to/file.l` against the program file's directory, then the current directory; pass `--import-root DIR` (repeatable) to search elsewhere.
125
125
  - `--load TABLE=PATH` (repeatable) loads a csv/tsv/json/jsonl/parquet file as a table before running, e.g. `synalog senior.l run Senior --load employees=employees.csv`.
126
126
 
127
- ### Projects: `synalog.toml`
127
+ ### Projects: `layer.toml`
128
128
 
129
- A folder with a `synalog.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
129
+ A folder with a `layer.toml` is a project. Its `[connection]` says which database its programs run on, as plain fields — commit it:
130
130
 
131
131
  ```toml
132
132
  [connection]
@@ -138,7 +138,7 @@ user = "analyst"
138
138
  schema = "public"
139
139
  ```
140
140
 
141
- Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database; `--engine`, an `@Engine` annotation, `--dsn` and `SYNALOG_<ENGINE>_DSN` still take precedence. The fields of every engine are in `synalog.project.ENGINES`.
141
+ Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Inside the project (from the program's folder or the current directory, and their parents), `run`, `print` and `verify` use that engine and connection, imports resolve from the project's folder, and `synalog introspect` writes the project's `tables/`, one file per table of the database. The project's file is the only place a connection comes from; `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. `synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. The fields of every engine are in `synalog.project.ENGINES`.
142
142
 
143
143
  Running `synalog` with no arguments starts an interactive session, in the spirit of `python` (the options above, e.g. `--engine` or `--load`, apply to it too):
144
144
 
@@ -220,7 +220,7 @@ for name, sql in sqls.items():
220
220
  print(name, sql)
221
221
  ```
222
222
 
223
- ### `execute(source, predicate, engine=None, dsn=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
223
+ ### `execute(source, predicate, engine=None, project=None, import_root=None, limit=None, offset=None, pattern=None, assertion=None, loads=()) -> tuple[list[str], list[tuple]]`
224
224
 
225
225
  Run a predicate on its database (the program's or project's engine and connection) and return `(columns, rows)`. Each recursion stops as soon as a step changes nothing, so it costs the steps its data needs, and `@Recursive(P, -1)` (until nothing changes) runs on every engine. `plan()` returns the same steps for a host that runs them itself.
226
226
 
@@ -228,9 +228,9 @@ Run a predicate on its database (the program's or project's engine and connectio
228
228
  columns, rows = synalog.execute(source, "TopCustomers", limit=20)
229
229
  ```
230
230
 
231
- ### `check(source, engine=None, import_root=None, assertions=True, dsn=None) -> tuple[list[str], list[str]]`
231
+ ### `check(source, engine=None, import_root=None, assertions=True, project=None) -> tuple[list[str], list[str]]`
232
232
 
233
- Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `synalog.toml` has a `[connection]` (or given a `dsn`), it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
233
+ Run structural validation. Returns the error messages and the warning messages, as two lists. The program is valid when there is no error; warnings do not make it invalid. Inside a project whose `layer.toml` has a `[connection]` — the current directory's, or the folder `project` names — it also runs the program's `@Assert` statements against the database and reports each violated one as an error; `assertions=False` keeps it offline.
234
234
 
235
235
  ```python
236
236
  errors, warnings = synalog.check(source)
@@ -243,7 +243,7 @@ All of these functions accept an optional `engine` keyword that overrides the pr
243
243
 
244
244
  ## Language overview
245
245
 
246
- A Synalog program is a **project**: a folder with a `synalog.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
246
+ A Synalog program is a **project**: a folder with a `layer.toml`, holding one predicate per file in three folders, the layout of [semantic layers](https://github.com/SynaLinks/semantic-layers):
247
247
 
248
248
  ```
249
249
  shop/
@@ -251,7 +251,7 @@ shop/
251
251
  ├── concepts/Customer.l # entities and relationships extracted from tables
252
252
  ├── rules/CustomerRevenue.l # insights derived from concepts
253
253
  ├── rules/TopCustomers.l
254
- └── synalog.toml # the project's name, description and database
254
+ └── layer.toml # the project's name, description and database
255
255
  ```
256
256
 
257
257
  Each file is named after its predicate and opens with front matter (`name`, the predicate; `description`, what its rows are), then imports what it builds on by folder, file and predicate, then orders its predicate with `@OrderBy`. A database table is referenced by its lowercase database name in `tables/`, and everything else builds on the PascalCase predicate that maps it:
@@ -143,7 +143,7 @@ An assertion is checked by looking for its counterexamples: Synalog compiles tha
143
143
  | `synalog program.l verify [Predicate ...]` | Runs every assertion, or those of the given predicates, and prints the counterexamples of each violated one (5 by default, `--limit` to change). Exits 1 if one is violated. |
144
144
  | `synalog program.l run Predicate` | Checks the program's assertions before it prints anything, and exits 1 with a few counterexamples if one is violated. |
145
145
  | `synalog program.l print Predicate` | Never touches the database: assertions are not run. |
146
- | [`check()`](python-api.md#check) | Runs the assertions when a database is known: inside a project whose `synalog.toml` has a `[connection]`, or given a `dsn`. Each violated assertion is an error. `assertions=False` keeps it offline. |
146
+ | [`check()`](python-api.md#check) | Runs the assertions when a database is known: inside a project whose `layer.toml` has a `[connection]` (the current directory's, or the folder `project` names). Each violated assertion is an error. `assertions=False` keeps it offline. |
147
147
 
148
148
  ```text
149
149
  $ synalog family.l run Grandparent --load parents=parents.csv
@@ -55,13 +55,12 @@ $ synalog program.l run EngineeringTeam
55
55
  | Option | Meaning |
56
56
  | --- | --- |
57
57
  | `-c PROGRAM` | Pass the program text inline instead of `FILE`, like `python -c`. |
58
- | `--engine <name>` | Target SQL dialect. Resolution order: this flag, then the program's `@Engine` annotation, then `duckdb`. |
58
+ | `--engine <name>` | Target SQL dialect. Resolution order: this flag, then the program's `@Engine` annotation, then the project's engine, then `duckdb`. |
59
59
  | `--limit N` / `--offset N` | Paginate the result. |
60
60
  | `--csv` | With `run`: print results as CSV instead of a rendered table. |
61
61
  | `--search REGEX` | With `print`/`run`: keep only rows where some column matches the regular expression `REGEX` (engine-native regex, not a SQL `LIKE` pattern). Applies to the filtered rows before pagination. |
62
62
  | `--import-root DIR` | Directory where `import` statements look up `.l` files (repeatable). |
63
63
  | `--load TABLE=PATH` | Load a csv/tsv/json/jsonl/parquet file as a table before running (repeatable). |
64
- | `--dsn <conninfo>` | PostgreSQL connection string for `--engine psql` (or set `SYNALOG_PSQL_DSN`). |
65
64
 
66
65
  Passing `-` as the file reads the program from stdin, and `-c` takes the program text directly, and both compose with the other options:
67
66
 
@@ -79,7 +78,7 @@ With `-c` there is no `FILE` argument: the positionals are the command and its p
79
78
 
80
79
  - **duckdb**: the default engine, bundled with synalog; nothing extra to install.
81
80
  - **sqlite**: Python's stdlib driver; Logica's runtime UDFs (ArgMin/ArgMax, ARRAY_CONCAT, ...) are registered when the `logica` package is installed.
82
- - **psql**: bundled with synalog (psycopg with its own libpq); needs a connection string.
81
+ - **psql**: bundled with synalog (psycopg with its own libpq); runs in a [project](#projects-layertoml) connected to PostgreSQL.
83
82
 
84
83
  The [`Today` and `Now`](language/temporal.md) built-in concepts need no runner support, since the compiler inlines them per dialect, so they work on every engine.
85
84
 
@@ -100,7 +99,7 @@ duckdb and the PostgreSQL driver ship with synalog. For the other engines (`bigq
100
99
 
101
100
  ### Imports
102
101
 
103
- In a project, `import <folder>.<Name>.<Name>;` statements resolve from the project's folder (the one holding `synalog.toml`), whichever file runs and wherever from: `import tables.Orders.Orders;` reads the project's `tables/Orders.l`. The program file's directory and the current directory come next. Pass `--import-root DIR` (repeatable) to search elsewhere; explicit roots replace the defaults.
102
+ In a project, `import <folder>.<Name>.<Name>;` statements resolve from the project's folder (the one holding `layer.toml`), whichever file runs and wherever from: `import tables.Orders.Orders;` reads the project's `tables/Orders.l`. The program file's directory and the current directory come next. Pass `--import-root DIR` (repeatable) to search elsewhere; explicit roots replace the defaults.
104
103
 
105
104
  In the [`shop` project](language/index.md), `rules/TopCustomers.l` builds on `rules/CustomerRevenue.l`:
106
105
 
@@ -155,9 +154,9 @@ Compile error: No rules are defining 'Missing', but compilation was requested.
155
154
 
156
155
  A failing program never produces partial output: `run` either prints the table or the error.
157
156
 
158
- ## Projects: `synalog.toml`
157
+ ## Projects: `layer.toml`
159
158
 
160
- A Synalog program is a [project](language/index.md): a folder with a `synalog.toml`, its predicates in `tables/`, `concepts/` and `rules/`, one per file. The file names and describes the project, and its `[connection]` says which database it runs on, as plain fields — commit it:
159
+ A Synalog program is a [project](language/index.md): a folder with a `layer.toml`, its predicates in `tables/`, `concepts/` and `rules/`, one per file. The file names and describes the project, and its `[connection]` says which database it runs on, as plain fields — commit it:
161
160
 
162
161
  ```toml
163
162
  [project]
@@ -173,7 +172,22 @@ user = "analyst"
173
172
  schema = "public"
174
173
  ```
175
174
 
176
- Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Anywhere in the project, `run`, `print`, `verify` and `introspect` use that engine and connection, and [imports](#imports) resolve from the project's folder; `--engine`, an `@Engine` annotation, `--dsn` and `SYNALOG_<ENGINE>_DSN` still take precedence. The fields of every engine are in `synalog.project.ENGINES`.
175
+ Secrets never go in the file (synalog refuses it): each comes from the environment as `SYNALOG_<ENGINE>_<FIELD>` — `SYNALOG_PSQL_PASSWORD`, `SYNALOG_DATABRICKS_ACCESS_TOKEN`, or `GOOGLE_APPLICATION_CREDENTIALS` for BigQuery — usually from the project's `.env`, kept out of git. Anywhere in the project, `run`, `print`, `verify` and `introspect` use that engine and connection, and [imports](#imports) resolve from the project's folder. The project's file is the only place a connection comes from: `--engine` and an `@Engine` annotation choose the dialect, and a remote engine runs only through the project's connection to it. The fields of every engine are in `synalog.project.ENGINES`.
176
+
177
+ ### Connect
178
+
179
+ ```console
180
+ $ synalog connect psql host=db.example.com database=sales user=analyst password=...
181
+ Connected /path/to/sales/layer.toml to psql (secrets in .env)
182
+ $ synalog connect
183
+ /path/to/sales/layer.toml
184
+ engine = psql
185
+ host = db.example.com
186
+ ...
187
+ password: SYNALOG_PSQL_PASSWORD set
188
+ ```
189
+
190
+ `synalog connect <engine> key=value ...` writes it — the non-secret fields to `layer.toml`, the secret ones to `.env` — `synalog connect` shows it (secrets hidden), `synalog connect clear` removes it. It runs in the project's folder — the nearest `layer.toml`'s, else the current directory — keeps the file's other tables, adds `.env` to `.gitignore`, and writes nothing when a required field is missing.
177
191
 
178
192
  ### Introspect
179
193
 
@@ -193,7 +207,7 @@ description: Orders.
193
207
  PublicOrders(order_id:, customer_id:, amount:, status:) :- public.orders(order_id:, customer_id:, amount:, status:);
194
208
  ```
195
209
 
196
- Run it again when the schema changes: the declarations are regenerated, and a file's front matter (its description, keywords, ...) is kept as written. A table the database no longer has keeps its file, since concepts may import it, and is listed. Outside a project, `synalog introspect <engine> [dsn]` prints the declarations instead. PostgreSQL, Trino, Presto, Databricks and BigQuery can be introspected.
210
+ Run it again when the schema changes: the declarations are regenerated, and a file's front matter (its description, keywords, ...) is kept as written. A table the database no longer has keeps its file, since concepts may import it, and is listed. It needs a project connected to its database. PostgreSQL, Trino, Presto, Databricks and BigQuery can be introspected.
197
211
 
198
212
  ## Add the skill to your coding agent
199
213
 
@@ -243,7 +257,7 @@ Bad(x) :- x ==<EMPTY>
243
257
  Greeting("hi");
244
258
  ```
245
259
 
246
- The `--engine`, `--dsn`, `--import-root` and `--load` options also apply to the session:
260
+ The `--engine`, `--import-root` and `--load` options also apply to the session, and the project's connection too:
247
261
 
248
262
  ```bash
249
263
  synalog --engine sqlite --load employees=employees.csv
@@ -131,7 +131,7 @@ It deploys to GitHub Pages automatically on push via `.github/workflows/docs.yml
131
131
 
132
132
  ### Documentation examples
133
133
 
134
- The example projects (`docs/examples/shop/`, `docs/examples/smoke/`) are laid out as every Synalog project is: `tables/`, `concepts/`, `rules/`, a `synalog.toml`, and their data in `data/<table>.csv`. `run.py` loads the data into an in-memory DuckDB, checks every file, runs the predicate its front matter names, and writes the results to `<project>.log`.
134
+ The example projects (`docs/examples/shop/`, `docs/examples/smoke/`) are laid out as every Synalog project is: `tables/`, `concepts/`, `rules/`, a `layer.toml`, and their data in `data/<table>.csv`. `run.py` loads the data into an in-memory DuckDB, checks every file, runs the predicate its front matter names, and writes the results to `<project>.log`.
135
135
 
136
136
  The feature examples (aggregation, functors, recursion, ...) are single `.l` programs with their data as inline facts. For those, the `run.py` harness validates each program with `synalog.check()`, compiles every predicate listed in its `# run:` header, executes the SQL on an in-memory DuckDB, and writes the combined output to the matching `.log` file. A `# load: <file>.csv as <table>` header loads a CSV from `docs/examples/` into a DuckDB table before the predicates run. The `Today`/`Now` built-in concepts need no special handling, since the compiler inlines them per dialect:
137
137