ferro-orm 0.10.5__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 (266) hide show
  1. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/PERMISSIONS.md +2 -2
  2. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/pull_request_template.md +9 -1
  3. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/workflows/ci.yml +101 -3
  4. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/workflows/publish-docs.yml +3 -3
  5. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.gitignore +1 -1
  6. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/AGENTS.md +130 -1
  7. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/CHANGELOG.md +34 -0
  8. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/CONTRIBUTING.md +3 -3
  9. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/Cargo.lock +77 -251
  10. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/Cargo.toml +6 -1
  11. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/PKG-INFO +2 -2
  12. {ferro_orm-0.10.5 → 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.10.5/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/issues/sqlite-integer-decimal-hydrates-as-none.md +87 -0
  78. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/sqlite-null-hydrates-as-int-zero.md +1 -0
  79. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/configurable-column-storage-types.md +8 -6
  80. ferro_orm-0.12.0/docs/solutions/patterns/ddl-on-live-engine.md +68 -0
  81. ferro_orm-0.12.0/docs/solutions/patterns/ir-invariants.md +182 -0
  82. ferro_orm-0.12.0/docs/solutions/patterns/sqlite-alembic-reconnect-hydration-tests.md +58 -0
  83. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/typed-null-binds.md +34 -28
  84. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/pyproject.toml +8 -7
  85. ferro_orm-0.12.0/scripts/demo_queries.py +271 -0
  86. ferro_orm-0.10.5/scripts/demo_queries.py → ferro_orm-0.12.0/scripts/demo_queries_pre_v012.py +20 -22
  87. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/backend.rs +365 -11
  88. ferro_orm-0.12.0/src/codec.rs +517 -0
  89. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/connection.rs +43 -39
  90. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/__init__.py +40 -0
  91. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/_core.pyi +78 -11
  92. ferro_orm-0.12.0/src/ferro/_deprecations.py +100 -0
  93. ferro_orm-0.12.0/src/ferro/ir/__init__.py +13 -0
  94. ferro_orm-0.12.0/src/ferro/ir/compiler.py +439 -0
  95. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/metaclass.py +3 -0
  96. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/migrations/alembic.py +184 -14
  97. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/models.py +119 -73
  98. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/query/builder.py +100 -44
  99. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/query/nodes.py +86 -13
  100. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/raw.py +28 -22
  101. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/relations/__init__.py +2 -0
  102. ferro_orm-0.12.0/src/ferro/session.py +43 -0
  103. ferro_orm-0.12.0/src/ferro/state.py +146 -0
  104. ferro_orm-0.12.0/src/hydration.rs +57 -0
  105. ferro_orm-0.12.0/src/introspect.rs +334 -0
  106. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/lib.rs +43 -2
  107. ferro_orm-0.12.0/src/migrate.rs +1481 -0
  108. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/operations.rs +641 -596
  109. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/query.rs +311 -230
  110. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/schema.rs +370 -210
  111. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/state.rs +45 -0
  112. ferro_orm-0.12.0/tests/fixtures/ir_vectors/README.md +45 -0
  113. ferro_orm-0.12.0/tests/fixtures/ir_vectors/codec_registry_core_v1.json +100 -0
  114. ferro_orm-0.12.0/tests/fixtures/ir_vectors/query_user_compound_v1.json +58 -0
  115. ferro_orm-0.12.0/tests/fixtures/ir_vectors/schema_invoice_baseline_v1.json +101 -0
  116. ferro_orm-0.12.0/tests/fixtures/ir_vectors/schema_phase1_fixture_models_v1.json +215 -0
  117. ferro_orm-0.12.0/tests/fixtures/shadow_reports/postgres.json +47 -0
  118. ferro_orm-0.12.0/tests/fixtures/shadow_reports/sqlite.json +47 -0
  119. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_alembic_bridge.py +26 -0
  120. ferro_orm-0.12.0/tests/test_auto_migrate.py +582 -0
  121. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_connection.py +7 -4
  122. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_cross_emitter_parity.py +99 -3
  123. ferro_orm-0.12.0/tests/test_deprecated_operator_inventory.py +51 -0
  124. ferro_orm-0.12.0/tests/test_deprecations.py +74 -0
  125. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_docs_examples.py +39 -5
  126. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_documentation_features.py +27 -22
  127. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_hydration.py +71 -4
  128. ferro_orm-0.12.0/tests/test_ir_vectors_contract.py +334 -0
  129. ferro_orm-0.12.0/tests/test_migrate_plan.py +225 -0
  130. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_query_builder.py +51 -24
  131. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_query_typing.py +20 -10
  132. ferro_orm-0.12.0/tests/test_session.py +104 -0
  133. ferro_orm-0.12.0/tests/test_shadow_reports.py +112 -0
  134. ferro_orm-0.12.0/tests/test_static_contracts.py +8 -0
  135. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_typed_null_binds.py +42 -6
  136. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/uv.lock +90 -131
  137. ferro_orm-0.12.0/zensical.toml +145 -0
  138. ferro_orm-0.10.5/docs/TEST_RESULTS.md +0 -210
  139. ferro_orm-0.10.5/docs/api/exceptions.md +0 -15
  140. ferro_orm-0.10.5/docs/api/fields.md +0 -21
  141. ferro_orm-0.10.5/docs/api/model.md +0 -31
  142. ferro_orm-0.10.5/docs/api/query.md +0 -97
  143. ferro_orm-0.10.5/docs/api/raw-sql.md +0 -124
  144. ferro_orm-0.10.5/docs/api/relationships.md +0 -35
  145. ferro_orm-0.10.5/docs/api/transactions.md +0 -36
  146. ferro_orm-0.10.5/docs/api/utilities.md +0 -73
  147. ferro_orm-0.10.5/docs/changelog.md +0 -46
  148. ferro_orm-0.10.5/docs/coming-soon.md +0 -509
  149. ferro_orm-0.10.5/docs/concepts/architecture.md +0 -332
  150. ferro_orm-0.10.5/docs/concepts/identity-map.md +0 -285
  151. ferro_orm-0.10.5/docs/concepts/performance.md +0 -222
  152. ferro_orm-0.10.5/docs/concepts/type-safety.md +0 -183
  153. ferro_orm-0.10.5/docs/contributing.md +0 -3
  154. ferro_orm-0.10.5/docs/faq.md +0 -262
  155. ferro_orm-0.10.5/docs/getting-started/installation.md +0 -86
  156. ferro_orm-0.10.5/docs/getting-started/next-steps.md +0 -159
  157. ferro_orm-0.10.5/docs/getting-started/tutorial.md +0 -388
  158. ferro_orm-0.10.5/docs/guide/backend.md +0 -404
  159. ferro_orm-0.10.5/docs/guide/database.md +0 -340
  160. ferro_orm-0.10.5/docs/guide/migrations.md +0 -445
  161. ferro_orm-0.10.5/docs/guide/models-and-fields.md +0 -272
  162. ferro_orm-0.10.5/docs/guide/mutations.md +0 -478
  163. ferro_orm-0.10.5/docs/guide/queries.md +0 -395
  164. ferro_orm-0.10.5/docs/guide/relationships.md +0 -393
  165. ferro_orm-0.10.5/docs/guide/transactions.md +0 -380
  166. ferro_orm-0.10.5/docs/howto/multiple-databases.md +0 -74
  167. ferro_orm-0.10.5/docs/howto/pagination.md +0 -355
  168. ferro_orm-0.10.5/docs/howto/soft-deletes.md +0 -86
  169. ferro_orm-0.10.5/docs/howto/testing.md +0 -205
  170. ferro_orm-0.10.5/docs/howto/timestamps.md +0 -57
  171. ferro_orm-0.10.5/docs/index.md +0 -142
  172. ferro_orm-0.10.5/docs/migration-sqlalchemy.md +0 -173
  173. ferro_orm-0.10.5/docs/why-ferro.md +0 -206
  174. ferro_orm-0.10.5/mkdocs.yml +0 -170
  175. ferro_orm-0.10.5/src/ferro/state.py +0 -19
  176. ferro_orm-0.10.5/tests/test_auto_migrate.py +0 -200
  177. ferro_orm-0.10.5/tests/test_static_contracts.py +0 -8
  178. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  179. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  180. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/PYPI_CHECKLIST.md +0 -0
  181. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/PYPI_SETUP.md +0 -0
  182. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/generated/wheels.generated.yml +0 -0
  183. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/workflows/packaging-smoke.yml +0 -0
  184. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/workflows/publish.yml +0 -0
  185. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.github/workflows/release.yml +0 -0
  186. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.pre-commit-config.yaml +0 -0
  187. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/.python-version +0 -0
  188. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/LICENSE +0 -0
  189. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/brainstorms/2026-04-29-named-connections-role-routing-requirements.md +0 -0
  190. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-08-typed-query-predicates-requirements.md +0 -0
  191. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-13-configurable-column-storage-types-requirements.md +0 -0
  192. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-14-autogenerate-support-for-db-type-requirements.md +0 -0
  193. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/brainstorms/2026-05-25-annotated-strenum-cold-hydration-requirements.md +0 -0
  194. {ferro_orm-0.10.5/docs → ferro_orm-0.12.0/docs/pages}/stylesheets/extra.css +0 -0
  195. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-04-24-001-refactor-multi-db-backend-architecture-plan.md +0 -0
  196. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-04-29-001-typed-null-binds-plan.md +0 -0
  197. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-04-29-002-feat-named-connections-plan.md +0 -0
  198. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-05-07-001-refactor-generic-model-connection-plan.md +0 -0
  199. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-05-08-001-feat-typed-query-predicates-plan.md +0 -0
  200. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-05-13-001-feat-configurable-column-storage-types-plan.md +0 -0
  201. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/plans/2026-05-25-001-fix-annotated-strenum-cold-hydration-plan.md +0 -0
  202. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/README.md +0 -0
  203. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/pydantic-slots-missing-after-ferro-hydration.md +0 -0
  204. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/python-3.14-deferred-annotation-typeerror-swallow.md +0 -0
  205. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/sa-pk-column-nullable-divergence.md +0 -0
  206. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/sa-vs-rust-unique-constraint-shape.md +0 -0
  207. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/issues/typed-where-null-panics-is-null.md +0 -0
  208. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/cross-emitter-ddl-parity.md +0 -0
  209. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/foreign-key-index.md +0 -0
  210. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/index-unique-redundancy.md +0 -0
  211. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/docs/solutions/patterns/shadow-fk-columns.md +0 -0
  212. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/justfile +0 -0
  213. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/_annotation_utils.py +0 -0
  214. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/_shadow_fk_types.py +0 -0
  215. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/base.py +0 -0
  216. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/composite_indexes.py +0 -0
  217. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/composite_uniques.py +0 -0
  218. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/exceptions.py +0 -0
  219. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/fields.py +0 -0
  220. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/migrations/__init__.py +0 -0
  221. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/py.typed +0 -0
  222. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/query/__init__.py +0 -0
  223. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/relations/descriptors.py +0 -0
  224. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/ferro/schema_metadata.py +0 -0
  225. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/src/schema_bind.rs +0 -0
  226. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/__init__.py +0 -0
  227. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/conftest.py +0 -0
  228. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/db_backends.py +0 -0
  229. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_aggregation.py +0 -0
  230. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_alembic_autogenerate.py +0 -0
  231. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_alembic_db_type.py +0 -0
  232. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_alembic_nullability.py +0 -0
  233. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_alembic_type_mapping.py +0 -0
  234. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_bulk_update.py +0 -0
  235. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_composite_index.py +0 -0
  236. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_composite_unique.py +0 -0
  237. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_connection_redaction.py +0 -0
  238. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_constraints.py +0 -0
  239. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_crud.py +0 -0
  240. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_db_backends.py +0 -0
  241. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_db_type_cross_emitter_parity.py +0 -0
  242. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_db_type_integration.py +0 -0
  243. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_db_type_typing.py +0 -0
  244. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_db_type_validation.py +0 -0
  245. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_deletion.py +0 -0
  246. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_enum_cold_hydration.py +0 -0
  247. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_field_wrapper.py +0 -0
  248. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_helpers.py +0 -0
  249. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_metaclass_internals.py +0 -0
  250. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_metadata.py +0 -0
  251. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_models.py +0 -0
  252. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_named_connections_integration.py +0 -0
  253. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_one_to_one.py +0 -0
  254. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_raw_sql.py +0 -0
  255. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_refresh.py +0 -0
  256. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_relationship_engine.py +0 -0
  257. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_schema.py +0 -0
  258. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_schema_constraints.py +0 -0
  259. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_schema_db_type_metadata.py +0 -0
  260. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_schema_enum_annotations.py +0 -0
  261. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_shadow_fk_types.py +0 -0
  262. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_sqlite_alembic_reconnect_hydration.py +0 -0
  263. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_string_search.py +0 -0
  264. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_structural_types.py +0 -0
  265. {ferro_orm-0.10.5 → ferro_orm-0.12.0}/tests/test_temporal_types.py +0 -0
  266. {ferro_orm-0.10.5 → 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
@@ -32,7 +32,7 @@ For a single model, every emitter must agree on:
32
32
  includes the canonical `db_type` vocabulary (`text`, `varchar(N)`,
33
33
  `smallint`, `int`, `bigint`, `uuid`, `timestamp`, `timestamptz`, `date`,
34
34
  `time`) — duplicated in `_db_type_to_sa_type` (Python) and
35
- `apply_db_type_to_column_def` (Rust), pinned by
35
+ `db_type_token_to_canonical` (Rust), pinned by
36
36
  `tests/test_db_type_cross_emitter_parity.py`.
37
37
  4. **Index names** — `idx_<table>_<col>` for single-column indexes,
38
38
  `idx_<table>_<col1>_<col2>...` for composite indexes.
@@ -147,3 +147,132 @@ search this directory before starting work.
147
147
  `docs/solutions/issues/` — debugging stories and known footguns.
148
148
 
149
149
  See `docs/solutions/README.md` for the frontmatter conventions.
150
+
151
+ ---
152
+
153
+ ## I-6: No AI attribution in commits or PRs
154
+
155
+ Never sign commits or pull requests with AI/agent attribution. No
156
+ `Co-Authored-By: Claude ...` trailers, no "Generated with Claude Code"
157
+ footers, no robot emoji bylines — in commit messages, PR titles, or PR
158
+ bodies. This applies even when an agent's default behavior is to add them:
159
+ this rule overrides those defaults for this repository.
160
+
161
+ ---
162
+
163
+ ## I-7: No stop-gap solutions
164
+
165
+ Every feature, bug fix, and improvement must be designed as the best,
166
+ well-thought-out solution for the project with the library's future in
167
+ mind — as if time and money were no object. No stop-gaps, hacks,
168
+ quick-fixes, or otherwise lesser solves.
169
+
170
+ What this means in practice:
171
+
172
+ - **Prefer first-class, reusable primitives over local patches.** If a fix
173
+ only works for the immediate symptom while leaving the underlying
174
+ capability gap in place, build the capability instead. (Precedent:
175
+ `EngineHandle::refresh_pool()` was built as an engine-level schema-epoch
176
+ primitive rather than a migration-local statement-cache flush.)
177
+ - **Fail loudly over degrading silently.** "Skip with a warning and
178
+ continue", "best effort", and "documented residual risk" are not
179
+ acceptable resolutions for correctness gaps. Either the operation
180
+ succeeds completely or it aborts with a clear, actionable error.
181
+ - **Treat certain phrases as redesign triggers.** If a plan, comment, or PR
182
+ description contains "best-effort", "partial mitigation", "documented
183
+ residual risk", "good enough for now", "temporary workaround", or
184
+ "fallback if X turns out to be hard" — that part of the design is not
185
+ finished. Redesign it before presenting or implementing it.
186
+ - **Scoped-down is fine; hollowed-out is not.** Deliberately excluding
187
+ something from scope (with the boundary stated and a real path for the
188
+ excluded case, e.g. "renames are Alembic territory") is good design.
189
+ Shipping a half-working version of something that is *in* scope is not.
190
+
191
+ This rule binds human contributors and AI agents equally, and overrides any
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,40 @@
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
+
18
+ ## v0.11.0 (2026-06-11)
19
+
20
+ ### Chores
21
+
22
+ - Solution quality requirements
23
+ ([`c62770b`](https://github.com/syn54x/ferro-orm/commit/c62770b6d3073e8d596c30f29895bccddc2a4d7f))
24
+
25
+ ### Documentation
26
+
27
+ - Compound SQLite hydration learnings (#56, #58)
28
+ ([#60](https://github.com/syn54x/ferro-orm/pull/60),
29
+ [`b609e72`](https://github.com/syn54x/ferro-orm/commit/b609e7274e1886cc5d8564c7adb52fc3c79ffc8d))
30
+
31
+ ### Features
32
+
33
+ - Extend auto_migrate with column updates and destructive drops
34
+ ([#69](https://github.com/syn54x/ferro-orm/pull/69),
35
+ [`a76cb0f`](https://github.com/syn54x/ferro-orm/commit/a76cb0f2451dab79303353f2c77d74ca3e1ad4a5))
36
+
37
+
4
38
  ## v0.10.5 (2026-05-25)
5
39
 
6
40
  ### Bug Fixes
@@ -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
  ```