ferro-orm 0.11.0__tar.gz → 0.12.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 (265) hide show
  1. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/PERMISSIONS.md +2 -2
  2. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/pull_request_template.md +9 -1
  3. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/workflows/ci.yml +101 -3
  4. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/workflows/publish-docs.yml +3 -3
  5. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.gitignore +1 -1
  6. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/AGENTS.md +86 -0
  7. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/CHANGELOG.md +14 -0
  8. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/CONTRIBUTING.md +3 -3
  9. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/Cargo.lock +52 -225
  10. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/Cargo.toml +6 -1
  11. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/PKG-INFO +2 -2
  12. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/README.md +1 -1
  13. ferro_orm-0.12.0/crates/ferro-migrate/Cargo.toml +7 -0
  14. ferro_orm-0.12.0/crates/ferro-migrate/src/lib.rs +271 -0
  15. ferro_orm-0.12.0/crates/ferro-schema-ir/Cargo.toml +8 -0
  16. ferro_orm-0.12.0/crates/ferro-schema-ir/src/lib.rs +195 -0
  17. ferro_orm-0.12.0/docs/examples/multiple_databases.py +45 -0
  18. ferro_orm-0.12.0/docs/examples/mutations.py +49 -0
  19. ferro_orm-0.12.0/docs/examples/pagination.py +47 -0
  20. ferro_orm-0.12.0/docs/examples/predicates.py +92 -0
  21. ferro_orm-0.12.0/docs/examples/predicates_annotated.py +36 -0
  22. ferro_orm-0.12.0/docs/examples/quickstart.py +111 -0
  23. ferro_orm-0.12.0/docs/examples/quickstart_annotated.py +48 -0
  24. ferro_orm-0.12.0/docs/examples/raw_sql.py +43 -0
  25. ferro_orm-0.12.0/docs/examples/relationships.py +128 -0
  26. ferro_orm-0.12.0/docs/examples/relationships_annotated.py +108 -0
  27. ferro_orm-0.12.0/docs/examples/soft_deletes.py +61 -0
  28. ferro_orm-0.12.0/docs/examples/soft_deletes_annotated.py +58 -0
  29. ferro_orm-0.12.0/docs/examples/testing_conftest.py +33 -0
  30. ferro_orm-0.12.0/docs/examples/timestamps.py +53 -0
  31. ferro_orm-0.12.0/docs/examples/timestamps_annotated.py +50 -0
  32. ferro_orm-0.12.0/docs/examples/transactions.py +58 -0
  33. ferro_orm-0.12.0/docs/pages/api/connection.md +21 -0
  34. ferro_orm-0.12.0/docs/pages/api/exceptions.md +5 -0
  35. ferro_orm-0.12.0/docs/pages/api/fields.md +15 -0
  36. ferro_orm-0.12.0/docs/pages/api/migrations.md +7 -0
  37. ferro_orm-0.12.0/docs/pages/api/model.md +5 -0
  38. ferro_orm-0.12.0/docs/pages/api/queries.md +11 -0
  39. ferro_orm-0.12.0/docs/pages/api/raw-sql.md +9 -0
  40. ferro_orm-0.12.0/docs/pages/api/relationships.md +11 -0
  41. ferro_orm-0.12.0/docs/pages/api/transactions.md +7 -0
  42. ferro_orm-0.12.0/docs/pages/changelog.md +1 -0
  43. ferro_orm-0.12.0/docs/pages/concepts/architecture.md +234 -0
  44. ferro_orm-0.12.0/docs/pages/concepts/backends.md +107 -0
  45. ferro_orm-0.12.0/docs/pages/concepts/identity-map.md +151 -0
  46. ferro_orm-0.12.0/docs/pages/concepts/performance.md +108 -0
  47. {ferro_orm-0.11.0/docs → ferro_orm-0.12.0/docs/pages}/concepts/query-typing.md +34 -33
  48. ferro_orm-0.12.0/docs/pages/concepts/type-safety.md +110 -0
  49. ferro_orm-0.12.0/docs/pages/contributing.md +5 -0
  50. ferro_orm-0.12.0/docs/pages/faq.md +103 -0
  51. ferro_orm-0.12.0/docs/pages/getting-started/installation.md +76 -0
  52. ferro_orm-0.12.0/docs/pages/getting-started/next-steps.md +56 -0
  53. ferro_orm-0.12.0/docs/pages/getting-started/quickstart.md +103 -0
  54. ferro_orm-0.12.0/docs/pages/guide/connections.md +192 -0
  55. ferro_orm-0.12.0/docs/pages/guide/migrations.md +164 -0
  56. ferro_orm-0.12.0/docs/pages/guide/models-and-fields.md +477 -0
  57. ferro_orm-0.12.0/docs/pages/guide/mutations.md +145 -0
  58. ferro_orm-0.12.0/docs/pages/guide/queries.md +177 -0
  59. ferro_orm-0.12.0/docs/pages/guide/raw-sql.md +109 -0
  60. ferro_orm-0.12.0/docs/pages/guide/relationships.md +240 -0
  61. ferro_orm-0.12.0/docs/pages/guide/transactions.md +121 -0
  62. ferro_orm-0.12.0/docs/pages/howto/migrate-from-sqlalchemy.md +270 -0
  63. ferro_orm-0.12.0/docs/pages/howto/migrating-to-v0-12-0.md +147 -0
  64. ferro_orm-0.12.0/docs/pages/howto/multiple-databases.md +71 -0
  65. ferro_orm-0.12.0/docs/pages/howto/pagination.md +122 -0
  66. ferro_orm-0.12.0/docs/pages/howto/soft-deletes.md +63 -0
  67. ferro_orm-0.12.0/docs/pages/howto/testing.md +169 -0
  68. ferro_orm-0.12.0/docs/pages/howto/timestamps.md +56 -0
  69. ferro_orm-0.12.0/docs/pages/index.md +12 -0
  70. ferro_orm-0.12.0/docs/pages/roadmap.md +22 -0
  71. ferro_orm-0.12.0/docs/pages/why-ferro.md +89 -0
  72. ferro_orm-0.12.0/docs/plans/2026-06-19-001-ir-first-roadmap.md +626 -0
  73. ferro_orm-0.12.0/docs/plans/ir-first-migration-guide.md +165 -0
  74. ferro_orm-0.12.0/docs/plans/ir-first-release-checklist.md +46 -0
  75. ferro_orm-0.12.0/docs/rfc/ir-contracts-v1.md +228 -0
  76. ferro_orm-0.12.0/docs/solutions/architecture-patterns/ir-first-merge-readiness-review.md +235 -0
  77. ferro_orm-0.12.0/docs/solutions/patterns/ir-invariants.md +182 -0
  78. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/typed-null-binds.md +21 -28
  79. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/pyproject.toml +8 -7
  80. ferro_orm-0.12.0/scripts/demo_queries.py +271 -0
  81. ferro_orm-0.11.0/scripts/demo_queries.py → ferro_orm-0.12.0/scripts/demo_queries_pre_v012.py +20 -22
  82. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/backend.rs +16 -0
  83. ferro_orm-0.12.0/src/codec.rs +517 -0
  84. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/connection.rs +17 -2
  85. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/__init__.py +5 -0
  86. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/_core.pyi +44 -11
  87. ferro_orm-0.12.0/src/ferro/_deprecations.py +100 -0
  88. ferro_orm-0.12.0/src/ferro/ir/__init__.py +13 -0
  89. ferro_orm-0.12.0/src/ferro/ir/compiler.py +439 -0
  90. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/metaclass.py +3 -0
  91. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/migrations/alembic.py +184 -14
  92. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/models.py +119 -73
  93. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/query/builder.py +100 -44
  94. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/query/nodes.py +86 -13
  95. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/raw.py +28 -22
  96. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/relations/__init__.py +2 -0
  97. ferro_orm-0.12.0/src/ferro/session.py +43 -0
  98. ferro_orm-0.12.0/src/ferro/state.py +146 -0
  99. ferro_orm-0.12.0/src/hydration.rs +57 -0
  100. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/lib.rs +8 -0
  101. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/migrate.rs +214 -0
  102. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/operations.rs +600 -551
  103. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/query.rs +308 -223
  104. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/schema.rs +32 -0
  105. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/state.rs +45 -0
  106. ferro_orm-0.12.0/tests/fixtures/ir_vectors/README.md +45 -0
  107. ferro_orm-0.12.0/tests/fixtures/ir_vectors/codec_registry_core_v1.json +100 -0
  108. ferro_orm-0.12.0/tests/fixtures/ir_vectors/query_user_compound_v1.json +58 -0
  109. ferro_orm-0.12.0/tests/fixtures/ir_vectors/schema_invoice_baseline_v1.json +101 -0
  110. ferro_orm-0.12.0/tests/fixtures/ir_vectors/schema_phase1_fixture_models_v1.json +215 -0
  111. ferro_orm-0.12.0/tests/fixtures/shadow_reports/postgres.json +47 -0
  112. ferro_orm-0.12.0/tests/fixtures/shadow_reports/sqlite.json +47 -0
  113. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_alembic_bridge.py +26 -0
  114. ferro_orm-0.12.0/tests/test_deprecated_operator_inventory.py +51 -0
  115. ferro_orm-0.12.0/tests/test_deprecations.py +74 -0
  116. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_docs_examples.py +39 -5
  117. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_documentation_features.py +27 -22
  118. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_hydration.py +71 -4
  119. ferro_orm-0.12.0/tests/test_ir_vectors_contract.py +334 -0
  120. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_query_builder.py +51 -24
  121. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_query_typing.py +20 -10
  122. ferro_orm-0.12.0/tests/test_session.py +104 -0
  123. ferro_orm-0.12.0/tests/test_shadow_reports.py +112 -0
  124. ferro_orm-0.12.0/tests/test_static_contracts.py +8 -0
  125. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_typed_null_binds.py +42 -6
  126. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/uv.lock +90 -131
  127. ferro_orm-0.12.0/zensical.toml +145 -0
  128. ferro_orm-0.11.0/docs/TEST_RESULTS.md +0 -210
  129. ferro_orm-0.11.0/docs/api/exceptions.md +0 -15
  130. ferro_orm-0.11.0/docs/api/fields.md +0 -21
  131. ferro_orm-0.11.0/docs/api/model.md +0 -31
  132. ferro_orm-0.11.0/docs/api/query.md +0 -97
  133. ferro_orm-0.11.0/docs/api/raw-sql.md +0 -124
  134. ferro_orm-0.11.0/docs/api/relationships.md +0 -35
  135. ferro_orm-0.11.0/docs/api/transactions.md +0 -36
  136. ferro_orm-0.11.0/docs/api/utilities.md +0 -73
  137. ferro_orm-0.11.0/docs/changelog.md +0 -46
  138. ferro_orm-0.11.0/docs/coming-soon.md +0 -484
  139. ferro_orm-0.11.0/docs/concepts/architecture.md +0 -332
  140. ferro_orm-0.11.0/docs/concepts/identity-map.md +0 -285
  141. ferro_orm-0.11.0/docs/concepts/performance.md +0 -222
  142. ferro_orm-0.11.0/docs/concepts/type-safety.md +0 -183
  143. ferro_orm-0.11.0/docs/contributing.md +0 -3
  144. ferro_orm-0.11.0/docs/faq.md +0 -262
  145. ferro_orm-0.11.0/docs/getting-started/installation.md +0 -86
  146. ferro_orm-0.11.0/docs/getting-started/next-steps.md +0 -159
  147. ferro_orm-0.11.0/docs/getting-started/tutorial.md +0 -388
  148. ferro_orm-0.11.0/docs/guide/backend.md +0 -404
  149. ferro_orm-0.11.0/docs/guide/database.md +0 -385
  150. ferro_orm-0.11.0/docs/guide/migrations.md +0 -445
  151. ferro_orm-0.11.0/docs/guide/models-and-fields.md +0 -272
  152. ferro_orm-0.11.0/docs/guide/mutations.md +0 -478
  153. ferro_orm-0.11.0/docs/guide/queries.md +0 -395
  154. ferro_orm-0.11.0/docs/guide/relationships.md +0 -393
  155. ferro_orm-0.11.0/docs/guide/transactions.md +0 -380
  156. ferro_orm-0.11.0/docs/howto/multiple-databases.md +0 -74
  157. ferro_orm-0.11.0/docs/howto/pagination.md +0 -355
  158. ferro_orm-0.11.0/docs/howto/soft-deletes.md +0 -86
  159. ferro_orm-0.11.0/docs/howto/testing.md +0 -205
  160. ferro_orm-0.11.0/docs/howto/timestamps.md +0 -57
  161. ferro_orm-0.11.0/docs/index.md +0 -142
  162. ferro_orm-0.11.0/docs/migration-sqlalchemy.md +0 -173
  163. ferro_orm-0.11.0/docs/why-ferro.md +0 -206
  164. ferro_orm-0.11.0/mkdocs.yml +0 -170
  165. ferro_orm-0.11.0/src/ferro/state.py +0 -19
  166. ferro_orm-0.11.0/tests/test_static_contracts.py +0 -8
  167. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  168. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  169. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/PYPI_CHECKLIST.md +0 -0
  170. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/PYPI_SETUP.md +0 -0
  171. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/generated/wheels.generated.yml +0 -0
  172. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/workflows/packaging-smoke.yml +0 -0
  173. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/workflows/publish.yml +0 -0
  174. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.github/workflows/release.yml +0 -0
  175. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.pre-commit-config.yaml +0 -0
  176. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/.python-version +0 -0
  177. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/LICENSE +0 -0
  178. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/brainstorms/2026-04-29-named-connections-role-routing-requirements.md +0 -0
  179. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-08-typed-query-predicates-requirements.md +0 -0
  180. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-13-configurable-column-storage-types-requirements.md +0 -0
  181. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-14-autogenerate-support-for-db-type-requirements.md +0 -0
  182. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-25-annotated-strenum-cold-hydration-requirements.md +0 -0
  183. {ferro_orm-0.11.0/docs → ferro_orm-0.12.0/docs/pages}/stylesheets/extra.css +0 -0
  184. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-04-24-001-refactor-multi-db-backend-architecture-plan.md +0 -0
  185. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-04-29-001-typed-null-binds-plan.md +0 -0
  186. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-04-29-002-feat-named-connections-plan.md +0 -0
  187. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-05-07-001-refactor-generic-model-connection-plan.md +0 -0
  188. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-05-08-001-feat-typed-query-predicates-plan.md +0 -0
  189. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-05-13-001-feat-configurable-column-storage-types-plan.md +0 -0
  190. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/plans/2026-05-25-001-fix-annotated-strenum-cold-hydration-plan.md +0 -0
  191. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/README.md +0 -0
  192. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/pydantic-slots-missing-after-ferro-hydration.md +0 -0
  193. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/python-3.14-deferred-annotation-typeerror-swallow.md +0 -0
  194. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/sa-pk-column-nullable-divergence.md +0 -0
  195. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/sa-vs-rust-unique-constraint-shape.md +0 -0
  196. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/sqlite-integer-decimal-hydrates-as-none.md +0 -0
  197. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/sqlite-null-hydrates-as-int-zero.md +0 -0
  198. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/issues/typed-where-null-panics-is-null.md +0 -0
  199. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/configurable-column-storage-types.md +0 -0
  200. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/cross-emitter-ddl-parity.md +0 -0
  201. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/ddl-on-live-engine.md +0 -0
  202. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/foreign-key-index.md +0 -0
  203. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/index-unique-redundancy.md +0 -0
  204. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/shadow-fk-columns.md +0 -0
  205. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/docs/solutions/patterns/sqlite-alembic-reconnect-hydration-tests.md +0 -0
  206. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/justfile +0 -0
  207. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/_annotation_utils.py +0 -0
  208. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/_shadow_fk_types.py +0 -0
  209. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/base.py +0 -0
  210. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/composite_indexes.py +0 -0
  211. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/composite_uniques.py +0 -0
  212. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/exceptions.py +0 -0
  213. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/fields.py +0 -0
  214. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/migrations/__init__.py +0 -0
  215. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/py.typed +0 -0
  216. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/query/__init__.py +0 -0
  217. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/relations/descriptors.py +0 -0
  218. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/ferro/schema_metadata.py +0 -0
  219. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/introspect.rs +0 -0
  220. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/src/schema_bind.rs +0 -0
  221. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/__init__.py +0 -0
  222. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/conftest.py +0 -0
  223. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/db_backends.py +0 -0
  224. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_aggregation.py +0 -0
  225. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_alembic_autogenerate.py +0 -0
  226. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_alembic_db_type.py +0 -0
  227. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_alembic_nullability.py +0 -0
  228. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_alembic_type_mapping.py +0 -0
  229. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_auto_migrate.py +0 -0
  230. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_bulk_update.py +0 -0
  231. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_composite_index.py +0 -0
  232. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_composite_unique.py +0 -0
  233. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_connection.py +0 -0
  234. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_connection_redaction.py +0 -0
  235. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_constraints.py +0 -0
  236. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_cross_emitter_parity.py +0 -0
  237. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_crud.py +0 -0
  238. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_db_backends.py +0 -0
  239. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_db_type_cross_emitter_parity.py +0 -0
  240. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_db_type_integration.py +0 -0
  241. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_db_type_typing.py +0 -0
  242. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_db_type_validation.py +0 -0
  243. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_deletion.py +0 -0
  244. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_enum_cold_hydration.py +0 -0
  245. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_field_wrapper.py +0 -0
  246. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_helpers.py +0 -0
  247. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_metaclass_internals.py +0 -0
  248. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_metadata.py +0 -0
  249. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_migrate_plan.py +0 -0
  250. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_models.py +0 -0
  251. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_named_connections_integration.py +0 -0
  252. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_one_to_one.py +0 -0
  253. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_raw_sql.py +0 -0
  254. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_refresh.py +0 -0
  255. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_relationship_engine.py +0 -0
  256. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_schema.py +0 -0
  257. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_schema_constraints.py +0 -0
  258. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_schema_db_type_metadata.py +0 -0
  259. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_schema_enum_annotations.py +0 -0
  260. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_shadow_fk_types.py +0 -0
  261. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_sqlite_alembic_reconnect_hydration.py +0 -0
  262. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_string_search.py +0 -0
  263. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_structural_types.py +0 -0
  264. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_temporal_types.py +0 -0
  265. {ferro_orm-0.11.0 → ferro_orm-0.12.0}/tests/test_transactions.py +0 -0
@@ -133,12 +133,12 @@ permissions:
133
133
 
134
134
  **Why These Permissions:**
135
135
  - `pages: write` - Allows the workflow to:
136
- - Deploy the generated MkDocs static site to GitHub Pages
136
+ - Deploy the generated Zensical static site to GitHub Pages
137
137
  - Update the Pages deployment for the repository
138
138
  - `id-token: write` - Allows secure OIDC auth for Pages deployment
139
139
 
140
140
  **What It Does:**
141
- - Builds MkDocs docs
141
+ - Builds Zensical docs
142
142
  - Deploys docs to GitHub Pages
143
143
 
144
144
  ---
@@ -33,4 +33,12 @@
33
33
 
34
34
  ## Related Issues
35
35
 
36
- <!-- Example: Closes #123 -->
36
+ - Closes #
37
+ - Closes #
38
+
39
+ <!-- Use one line per completed issue: Closes #123 / Fixes #123 / Resolves #123 -->
40
+
41
+ ## Exit Steps
42
+
43
+ - [ ] Related completed issues are linked above with Closes/Fixes/Resolves
44
+ - [ ] Issue statuses are updated/auto-close on merge
@@ -3,9 +3,9 @@ name: CI
3
3
  on:
4
4
  workflow_call:
5
5
  pull_request:
6
- branches: [main]
6
+ branches: [main, feat/ir-first]
7
7
  push:
8
- branches: [main]
8
+ branches: [main, feat/ir-first]
9
9
  workflow_dispatch:
10
10
 
11
11
  concurrency:
@@ -16,6 +16,33 @@ permissions:
16
16
  contents: read
17
17
 
18
18
  jobs:
19
+ changed-shadow-paths:
20
+ name: Detect Shadow-Report Paths
21
+ runs-on: ubuntu-latest
22
+ if: github.event_name == 'pull_request'
23
+ outputs:
24
+ requires_shadow_reports: ${{ steps.filter.outputs.shadow }}
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v4
28
+
29
+ - name: Detect planner/IR path changes
30
+ id: filter
31
+ uses: dorny/paths-filter@v3
32
+ with:
33
+ filters: |
34
+ shadow:
35
+ - 'src/operations.rs'
36
+ - 'src/query.rs'
37
+ - 'src/schema.rs'
38
+ - 'src/migrate.rs'
39
+ - 'src/backend.rs'
40
+ - 'src/connection.rs'
41
+ - 'src/ferro/ir/**'
42
+ - 'crates/ferro-schema-ir/**'
43
+ - 'tests/test_shadow_reports.py'
44
+ - 'tests/fixtures/shadow_reports/**'
45
+
19
46
  lint-and-format:
20
47
  name: Lint & Format (Pre-commit / Prek)
21
48
  runs-on: ubuntu-latest
@@ -126,6 +153,10 @@ jobs:
126
153
  run: |
127
154
  uv run maturin develop
128
155
 
156
+ - name: Run IR vector contract harness
157
+ run: |
158
+ uv run pytest -v tests/test_ir_vectors_contract.py
159
+
129
160
  - name: Run pytest
130
161
  run: |
131
162
  uv run pytest -v --cov=src --cov-report=xml --cov-report=term
@@ -169,6 +200,10 @@ jobs:
169
200
  run: |
170
201
  uv run maturin develop
171
202
 
203
+ - name: Run IR vector contract harness
204
+ run: |
205
+ uv run pytest -v tests/test_ir_vectors_contract.py
206
+
172
207
  - name: Run pytest
173
208
  run: |
174
209
  uv run pytest -v --cov=src --cov-report=xml --cov-report=term
@@ -236,6 +271,64 @@ jobs:
236
271
  run: |
237
272
  uv run pytest -v -m "backend_matrix or postgres_only" --db-backends=sqlite,postgres
238
273
 
274
+ test-shadow-reports-pr:
275
+ name: Shadow reports (touched paths)
276
+ runs-on: ubuntu-latest
277
+ needs: [changed-shadow-paths]
278
+ if: github.event_name == 'pull_request' && needs.changed-shadow-paths.outputs.requires_shadow_reports == 'true'
279
+ services:
280
+ postgres:
281
+ image: postgres:17
282
+ env:
283
+ POSTGRES_USER: ferro
284
+ POSTGRES_PASSWORD: ferro
285
+ POSTGRES_DB: ferro
286
+ ports:
287
+ - 5432:5432
288
+ options: >-
289
+ --health-cmd "pg_isready -U ferro -d ferro"
290
+ --health-interval 10s
291
+ --health-timeout 5s
292
+ --health-retries 5
293
+ env:
294
+ FERRO_SUPABASE_URL: postgresql://ferro:ferro@127.0.0.1:5432/ferro?sslmode=disable
295
+ FERRO_SHADOW_RUNTIME: "1"
296
+ FERRO_SHADOW_RUNTIME_STRICT: "1"
297
+ steps:
298
+ - name: Checkout repository
299
+ uses: actions/checkout@v4
300
+
301
+ - name: Set up Python
302
+ uses: actions/setup-python@v5
303
+ with:
304
+ python-version: '3.13'
305
+
306
+ - name: Install UV
307
+ uses: astral-sh/setup-uv@v5
308
+ with:
309
+ enable-cache: true
310
+
311
+ - name: Set up Rust
312
+ uses: dtolnay/rust-toolchain@stable
313
+
314
+ - name: Cache Rust build
315
+ uses: Swatinem/rust-cache@v2
316
+ with:
317
+ prefix-key: v1
318
+ cache-on-failure: true
319
+
320
+ - name: Install dependencies
321
+ run: |
322
+ uv sync --only-group ci-test --no-install-project --python 3.13
323
+
324
+ - name: Build Rust extension
325
+ run: |
326
+ uv run maturin develop
327
+
328
+ - name: Verify stable shadow reports
329
+ run: |
330
+ uv run pytest -v tests/test_shadow_reports.py::test_shadow_report_fixture_stable --db-backends=sqlite,postgres
331
+
239
332
  check-conventional-commits:
240
333
  name: Check Conventional Commits
241
334
  runs-on: ubuntu-latest
@@ -285,7 +378,7 @@ jobs:
285
378
 
286
379
  all-checks:
287
380
  name: All Checks Passed
288
- needs: [lint-and-format, test-python-pr, test-python-main, test-python-backend-matrix, test-rust]
381
+ needs: [changed-shadow-paths, lint-and-format, test-python-pr, test-python-main, test-python-backend-matrix, test-shadow-reports-pr, test-rust]
289
382
  runs-on: ubuntu-latest
290
383
  if: always()
291
384
  steps:
@@ -294,10 +387,15 @@ jobs:
294
387
  run: |
295
388
  ok() { [[ "$1" == "success" || "$1" == "skipped" ]]; }
296
389
 
390
+ if [[ "${{ needs.changed-shadow-paths.result }}" == "failure" ]]; then
391
+ echo "Shadow path detection failed; refusing to treat shadow gate as passed."
392
+ exit 1
393
+ fi
297
394
  if ! ok "${{ needs.lint-and-format.result }}"; then exit 1; fi
298
395
  if ! ok "${{ needs.test-python-pr.result }}"; then exit 1; fi
299
396
  if ! ok "${{ needs.test-python-main.result }}"; then exit 1; fi
300
397
  if ! ok "${{ needs.test-python-backend-matrix.result }}"; then exit 1; fi
398
+ if ! ok "${{ needs.test-shadow-reports-pr.result }}"; then exit 1; fi
301
399
  if ! ok "${{ needs.test-rust.result }}"; then exit 1; fi
302
400
 
303
401
  echo "All checks passed!"
@@ -18,7 +18,7 @@ permissions:
18
18
 
19
19
  jobs:
20
20
  build-docs:
21
- name: Build docs (MkDocs)
21
+ name: Build docs (Zensical)
22
22
  runs-on: ubuntu-latest
23
23
  steps:
24
24
  - name: Checkout repository
@@ -40,9 +40,9 @@ jobs:
40
40
  run: |
41
41
  uv sync --only-group docs --no-install-project --python 3.13
42
42
 
43
- - name: Build MkDocs site
43
+ - name: Build Zensical site
44
44
  run: |
45
- uv run --no-sync mkdocs build
45
+ uv run --no-sync zensical build --clean
46
46
 
47
47
  - name: Upload Pages artifact
48
48
  uses: actions/upload-pages-artifact@v3
@@ -189,7 +189,7 @@ venv.bak/
189
189
  # Rope project settings
190
190
  .ropeproject
191
191
 
192
- # mkdocs documentation
192
+ # zensical documentation
193
193
  /site
194
194
 
195
195
  # mypy
@@ -190,3 +190,89 @@ What this means in practice:
190
190
 
191
191
  This rule binds human contributors and AI agents equally, and overrides any
192
192
  agent default that biases toward minimal or expedient changes.
193
+
194
+ ---
195
+
196
+ ## I-8: Docs examples show both field-declaration styles
197
+
198
+ Ferro supports two equivalent ways to declare model fields: assignment
199
+ (`name: str = Field(unique=True)`) and `Annotated` metadata
200
+ (`name: Annotated[str, Field(unique=True)]`). Every documentation example
201
+ that declares model fields with `Field()`/`FerroField()` options must show
202
+ **both** styles, side by side, as content tabs:
203
+
204
+ === "Assignment"
205
+
206
+ ```python
207
+ --8<-- "docs/examples/<example>.py:models"
208
+ ```
209
+
210
+ === "Annotated"
211
+
212
+ ```python
213
+ --8<-- "docs/examples/<example>_annotated.py:models"
214
+ ```
215
+
216
+ Rules:
217
+
218
+ - Both tabs must be backed by real, runnable code. Snippet-embedded model
219
+ definitions get a runnable `<name>_annotated.py` companion in
220
+ `docs/examples/` (exercised by `tests/test_docs_examples.py`); inline
221
+ blocks are written in both styles and compile-checked by the same test.
222
+ - Constructs with only one valid form appear identically in both tabs and
223
+ are not tabbed on their own: forward FKs are always
224
+ `Annotated[Target, ForeignKey(...)]`, and `BackRef()` / `ManyToMany()`
225
+ are always assignments.
226
+ - Code blocks that do not declare fields (queries, mutations, transactions,
227
+ usage snippets) are not affected by this rule.
228
+
229
+ This keeps users from ever wondering whether something is possible in their
230
+ preferred declaration style.
231
+
232
+ ---
233
+
234
+ ## I-9: Lambda predicates are the official query style
235
+
236
+ Documentation and examples use the lambda predicate style for all queries:
237
+
238
+ ```python
239
+ adults = await User.where(lambda t: t.age >= 18).all()
240
+ ```
241
+
242
+ Rules:
243
+
244
+ - **Every query example** in docs, docstring `Examples:` sections, and
245
+ `docs/examples/` scripts uses lambda predicates.
246
+ - When the predicate styles themselves are documented, present them in
247
+ order **lambda > `col()` > operator**, with lambda labeled the officially
248
+ recommended style.
249
+ - **Operator style** (`User.where(User.age >= 18)`) is compatible today but
250
+ is slated for deprecation in a future release and fails static type
251
+ checking (`User.age >= 18` types as `bool`; `where()` expects
252
+ `QueryNode | Predicate`). Docs say so explicitly wherever the style is
253
+ shown.
254
+ - **`order_by` is not a predicate** and keeps attribute style
255
+ (`order_by(User.age, "desc")`). Passing a lambda to `order_by` silently
256
+ produces a junk column name — never show it.
257
+
258
+ The canonical comparisons live in `docs/pages/guide/queries.md`
259
+ ("Predicate Styles") and `docs/pages/concepts/query-typing.md`; everywhere
260
+ else uses lambda without restating the trade-offs.
261
+
262
+ ---
263
+
264
+ ## I-10: PRs must close scoped issues explicitly
265
+
266
+ Issue closure is part of feature completion, not optional cleanup.
267
+
268
+ Rules:
269
+
270
+ - Every PR that completes scoped work **must** include GitHub auto-close
271
+ keywords in the PR body for each completed issue, e.g.
272
+ `Closes #89`, `Fixes #90`, `Resolves #91`.
273
+ - Do not rely on manual post-merge issue triage when the work is already done
274
+ in the PR; encode closure directly in the PR body.
275
+ - PRs must include an explicit exit-steps checklist item confirming issue status
276
+ updates are complete before merge.
277
+ - AI agents and human contributors follow the same requirement. If issue status
278
+ closure is missing, the PR is not done.
@@ -1,6 +1,20 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.12.0 (2026-06-23)
5
+
6
+ ### Documentation
7
+
8
+ - Rewrite site on Zensical with runnable examples
9
+ ([#70](https://github.com/syn54x/ferro-orm/pull/70),
10
+ [`da86911`](https://github.com/syn54x/ferro-orm/commit/da869118c55314363139a021c139a52a2bc8d1fb))
11
+
12
+ ### Features
13
+
14
+ - IR-first architecture program (Phases 0–7) ([#116](https://github.com/syn54x/ferro-orm/pull/116),
15
+ [`1c22857`](https://github.com/syn54x/ferro-orm/commit/1c228577aa663c4630a5c8a33f0bb000ebe288b2))
16
+
17
+
4
18
  ## v0.11.0 (2026-06-11)
5
19
 
6
20
  ### Chores
@@ -37,7 +37,7 @@ This will install all development dependencies including:
37
37
  - Testing tools (pytest, pytest-asyncio, pytest-cov)
38
38
  - Linting and formatting tools (ruff, prek)
39
39
  - Build tools (maturin)
40
- - Documentation tools (mkdocs-material)
40
+ - Documentation tools (zensical)
41
41
  - Release tools (commitizen, python-semantic-release)
42
42
 
43
43
  ### 3. Install Pre-commit Hooks
@@ -102,10 +102,10 @@ cargo clippy # Rust linting
102
102
 
103
103
  ```bash
104
104
  # Serve documentation locally (with live reload)
105
- uv run mkdocs serve
105
+ uv run zensical serve
106
106
 
107
107
  # Build documentation
108
- uv run mkdocs build
108
+ uv run zensical build
109
109
 
110
110
  # Documentation will be available at http://127.0.0.1:8000/
111
111
  ```