ferro-orm 0.12.3__tar.gz → 0.14.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 (353) hide show
  1. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/workflows/ci.yml +5 -2
  2. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/AGENTS.md +48 -25
  3. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/CHANGELOG.md +187 -0
  4. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/Cargo.lock +180 -32
  5. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/Cargo.toml +11 -3
  6. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/PKG-INFO +4 -4
  7. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/README.md +3 -3
  8. ferro_orm-0.14.0/benchmarks/README.md +133 -0
  9. ferro_orm-0.14.0/benchmarks/__init__.py +11 -0
  10. ferro_orm-0.14.0/benchmarks/__main__.py +10 -0
  11. ferro_orm-0.14.0/benchmarks/backends.py +88 -0
  12. ferro_orm-0.14.0/benchmarks/baselines/postgres.json +49 -0
  13. ferro_orm-0.14.0/benchmarks/baselines/sqlite.json +49 -0
  14. ferro_orm-0.14.0/benchmarks/compare.py +98 -0
  15. ferro_orm-0.14.0/benchmarks/harness.py +99 -0
  16. ferro_orm-0.14.0/benchmarks/model.py +84 -0
  17. ferro_orm-0.14.0/benchmarks/run.py +270 -0
  18. {ferro_orm-0.12.3/crates/ferro-migrate → ferro_orm-0.14.0/crates/ferro-ddl-lowering}/Cargo.toml +3 -1
  19. ferro_orm-0.14.0/crates/ferro-ddl-lowering/src/lib.rs +1509 -0
  20. ferro_orm-0.14.0/crates/ferro-migrate/Cargo.toml +10 -0
  21. ferro_orm-0.14.0/crates/ferro-migrate/src/emit.rs +712 -0
  22. ferro_orm-0.14.0/crates/ferro-migrate/src/lib.rs +322 -0
  23. ferro_orm-0.14.0/crates/ferro-migrate/src/tests.rs +1461 -0
  24. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/crates/ferro-schema-ir/src/lib.rs +101 -2
  25. ferro_orm-0.14.0/docs/brainstorms/2026-06-24-ir-p8-119-wire-automigrate-requirements.md +116 -0
  26. ferro_orm-0.14.0/docs/brainstorms/2026-06-25-ir-p8-120-parity-gate-requirements.md +119 -0
  27. ferro_orm-0.14.0/docs/brainstorms/2026-06-26-ir-p8.5-140-shared-lowering-requirements.md +111 -0
  28. ferro_orm-0.14.0/docs/brainstorms/2026-06-26-ir-p8.5-143-db-type-drop-requirements.md +116 -0
  29. ferro_orm-0.14.0/docs/brainstorms/2026-06-26-ir-p8.5-144-reconcile-indexes-requirements.md +130 -0
  30. ferro_orm-0.14.0/docs/brainstorms/2026-06-28-ir-p8.5-141-single-schema-ir-producer-requirements.md +166 -0
  31. ferro_orm-0.14.0/docs/brainstorms/2026-06-29-ir-p8.6-146-dialect-enum-unification-requirements.md +264 -0
  32. ferro_orm-0.14.0/docs/brainstorms/2026-06-29-ir-p8.6-153-create-path-ir-requirements.md +331 -0
  33. ferro_orm-0.14.0/docs/brainstorms/2026-06-30-ir-p8.6-155-deferred-annotations-requirements.md +338 -0
  34. ferro_orm-0.14.0/docs/brainstorms/2026-06-30-ir-p8.6-158-check-renderer-requirements.md +358 -0
  35. ferro_orm-0.14.0/docs/brainstorms/2026-07-01-ir-p8.6-154-datetime-tz-coarseness-requirements.md +327 -0
  36. ferro_orm-0.14.0/docs/brainstorms/2026-07-01-ir-p8.6-162-typed-save-bind-requirements.md +307 -0
  37. ferro_orm-0.14.0/docs/brainstorms/2026-07-01-ir-p8.6-165-blob-introspection-false-positive-requirements.md +353 -0
  38. ferro_orm-0.14.0/docs/brainstorms/2026-07-02-ff-c1-codec-plan-design.md +150 -0
  39. ferro_orm-0.14.0/docs/brainstorms/2026-07-06-upgrade-guide-consolidation-requirements.md +222 -0
  40. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/multiple_databases.py +9 -7
  41. ferro_orm-0.14.0/docs/examples/mutations.py +112 -0
  42. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/pagination.py +12 -10
  43. ferro_orm-0.14.0/docs/examples/predicates.py +84 -0
  44. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/predicates_annotated.py +13 -11
  45. ferro_orm-0.14.0/docs/examples/quickstart.py +112 -0
  46. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/quickstart_annotated.py +8 -7
  47. ferro_orm-0.14.0/docs/examples/raw_sql.py +45 -0
  48. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/relationships.py +37 -36
  49. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/relationships_annotated.py +19 -18
  50. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/soft_deletes.py +12 -11
  51. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/soft_deletes_annotated.py +10 -9
  52. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/testing_conftest.py +3 -2
  53. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/timestamps.py +10 -9
  54. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/examples/timestamps_annotated.py +8 -7
  55. ferro_orm-0.14.0/docs/examples/transactions.py +59 -0
  56. ferro_orm-0.14.0/docs/pages/api/exceptions.md +57 -0
  57. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/migrations.md +1 -1
  58. ferro_orm-0.14.0/docs/pages/api/queries.md +9 -0
  59. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/concepts/architecture.md +17 -6
  60. ferro_orm-0.14.0/docs/pages/concepts/identity-map.md +227 -0
  61. ferro_orm-0.14.0/docs/pages/concepts/query-typing.md +56 -0
  62. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/concepts/type-safety.md +2 -2
  63. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/faq.md +3 -1
  64. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/getting-started/quickstart.md +3 -3
  65. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/connections.md +2 -2
  66. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/migrations.md +3 -0
  67. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/models-and-fields.md +64 -1
  68. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/mutations.md +72 -16
  69. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/queries.md +49 -42
  70. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/migrate-from-sqlalchemy.md +4 -4
  71. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/multiple-databases.md +5 -3
  72. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/pagination.md +2 -2
  73. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/soft-deletes.md +1 -1
  74. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/testing.md +7 -5
  75. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/howto/timestamps.md +13 -1
  76. ferro_orm-0.14.0/docs/pages/howto/upgrade-guide.md +446 -0
  77. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-06-19-001-ir-first-roadmap.md +244 -24
  78. ferro_orm-0.14.0/docs/plans/2026-06-24-001-feat-ir-p8-119-wire-automigrate-plan.md +301 -0
  79. ferro_orm-0.14.0/docs/plans/2026-06-25-001-feat-ir-p8-120-parity-gate-plan.md +286 -0
  80. ferro_orm-0.14.0/docs/plans/2026-06-26-001-feat-ir-p8.5-140-shared-lowering-plan.md +311 -0
  81. ferro_orm-0.14.0/docs/plans/2026-06-26-002-feat-ir-p8.5-143-db-type-drop-plan.md +229 -0
  82. ferro_orm-0.14.0/docs/plans/2026-06-26-003-feat-ir-p8.5-144-reconcile-indexes-plan.md +503 -0
  83. ferro_orm-0.14.0/docs/plans/2026-06-28-001-feat-ir-p8.5-141-single-schema-ir-producer-plan.md +394 -0
  84. ferro_orm-0.14.0/docs/plans/2026-06-29-001-refactor-ir-p8.6-153-create-path-unification-plan.md +362 -0
  85. ferro_orm-0.14.0/docs/plans/2026-06-29-002-refactor-ir-p8.6-146-dialect-enum-unification-plan.md +388 -0
  86. ferro_orm-0.14.0/docs/plans/2026-06-30-001-refactor-ir-p8.6-158-check-renderer-plan.md +670 -0
  87. ferro_orm-0.14.0/docs/plans/2026-06-30-002-fix-ir-p8.6-155-deferred-annotations-plan.md +341 -0
  88. ferro_orm-0.14.0/docs/plans/2026-07-01-001-feat-ir-p8.6-162-typed-save-bind-plan.md +917 -0
  89. ferro_orm-0.14.0/docs/plans/2026-07-01-002-fix-ir-p8.6-154-datetime-tz-coarseness-plan.md +467 -0
  90. ferro_orm-0.14.0/docs/plans/2026-07-01-003-fix-ir-p8.6-165-blob-introspection-plan.md +307 -0
  91. ferro_orm-0.14.0/docs/plans/2026-07-02-001-fable-fixes-roadmap.md +449 -0
  92. ferro_orm-0.14.0/docs/plans/2026-07-02-002-feat-ff-a-172-typed-exceptions-plan.md +37 -0
  93. ferro_orm-0.14.0/docs/plans/2026-07-02-003-ff-c-native-decode-design.md +105 -0
  94. ferro_orm-0.14.0/docs/plans/2026-07-02-004-ff-c-c2-catalog-cache-design.md +159 -0
  95. ferro_orm-0.14.0/docs/plans/2026-07-02-005-ff-d-identity-routing-design.md +207 -0
  96. ferro_orm-0.14.0/docs/plans/2026-07-02-006-ff-d-identity-routing-plan.md +1474 -0
  97. ferro_orm-0.14.0/docs/plans/2026-07-03-007-ff-e-registry-identity-design.md +267 -0
  98. ferro_orm-0.14.0/docs/plans/2026-07-03-008-ff-e-registry-identity-plan.md +1584 -0
  99. ferro_orm-0.14.0/docs/plans/2026-07-06-009-ff-f-query-builder-1.0-design.md +152 -0
  100. ferro_orm-0.14.0/docs/plans/2026-07-06-010-ff-f-query-builder-1.0-plan.md +1290 -0
  101. ferro_orm-0.14.0/docs/plans/2026-07-06-011-ff-g-hardening-design.md +268 -0
  102. ferro_orm-0.14.0/docs/plans/2026-07-06-012-ff-g-hardening-plan.md +985 -0
  103. ferro_orm-0.14.0/docs/plans/2026-07-06-013-ff-g2-operations-dedup-design.md +193 -0
  104. ferro_orm-0.14.0/docs/plans/2026-07-06-014-ff-g2-operations-dedup-plan.md +1203 -0
  105. ferro_orm-0.14.0/docs/plans/2026-07-06-015-docs-upgrade-guide-consolidation-plan.md +518 -0
  106. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/ir-first-migration-guide.md +21 -4
  107. ferro_orm-0.14.0/docs/solutions/architecture-patterns/ir-first-lowering-consolidation-audit.md +246 -0
  108. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/architecture-patterns/ir-first-merge-readiness-review.md +1 -0
  109. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/sa-pk-column-nullable-divergence.md +10 -1
  110. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/sa-vs-rust-unique-constraint-shape.md +13 -1
  111. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/cross-emitter-ddl-parity.md +37 -9
  112. ferro_orm-0.14.0/docs/solutions/patterns/derived-type-and-naming-decision-table.md +161 -0
  113. ferro_orm-0.14.0/docs/solutions/patterns/persistence-state.md +76 -0
  114. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/justfile +6 -0
  115. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/pyproject.toml +2 -4
  116. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/backend.rs +280 -44
  117. ferro_orm-0.14.0/src/codec.rs +523 -0
  118. ferro_orm-0.14.0/src/codec_plan.rs +591 -0
  119. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/connection.rs +56 -28
  120. ferro_orm-0.14.0/src/errors.rs +181 -0
  121. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/__init__.py +113 -9
  122. ferro_orm-0.14.0/src/ferro/_bind_payload.py +51 -0
  123. ferro_orm-0.14.0/src/ferro/_core.pyi +236 -0
  124. ferro_orm-0.14.0/src/ferro/exceptions.py +134 -0
  125. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/ir/compiler.py +56 -46
  126. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/metaclass.py +118 -25
  127. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/migrations/alembic.py +99 -76
  128. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/models.py +224 -144
  129. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/query/__init__.py +1 -2
  130. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/query/builder.py +183 -206
  131. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/query/nodes.py +61 -129
  132. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/raw.py +17 -24
  133. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/relations/__init__.py +36 -17
  134. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/relations/descriptors.py +3 -11
  135. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/schema_metadata.py +20 -1
  136. ferro_orm-0.14.0/src/ferro/state.py +217 -0
  137. ferro_orm-0.14.0/src/hydration.rs +286 -0
  138. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/introspect.rs +109 -8
  139. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/lib.rs +36 -5
  140. ferro_orm-0.14.0/src/migrate.rs +2438 -0
  141. ferro_orm-0.14.0/src/naming_ffi.rs +92 -0
  142. ferro_orm-0.14.0/src/operations.rs +3856 -0
  143. ferro_orm-0.14.0/src/query.rs +558 -0
  144. ferro_orm-0.14.0/src/schema.rs +622 -0
  145. ferro_orm-0.14.0/src/schema_bind.rs +23 -0
  146. ferro_orm-0.14.0/src/state.rs +641 -0
  147. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/conftest.py +25 -0
  148. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/fixtures/ir_vectors/schema_invoice_baseline_v1.json +11 -8
  149. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/fixtures/ir_vectors/schema_phase1_fixture_models_v1.json +118 -16
  150. ferro_orm-0.14.0/tests/fixtures/shadow_reports/postgres.json +32 -0
  151. ferro_orm-0.14.0/tests/fixtures/shadow_reports/sqlite.json +32 -0
  152. ferro_orm-0.14.0/tests/static_fixtures/bad_predicates.py +19 -0
  153. ferro_orm-0.14.0/tests/static_fixtures/good_predicates.py +20 -0
  154. ferro_orm-0.14.0/tests/test_aggregation.py +74 -0
  155. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_alembic_autogenerate.py +13 -10
  156. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_alembic_bridge.py +45 -17
  157. ferro_orm-0.14.0/tests/test_auto_migrate.py +1313 -0
  158. ferro_orm-0.14.0/tests/test_bulk_update.py +66 -0
  159. ferro_orm-0.14.0/tests/test_catalog_cache.py +244 -0
  160. ferro_orm-0.14.0/tests/test_codec_plan.py +81 -0
  161. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_composite_index.py +21 -17
  162. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_composite_unique.py +19 -17
  163. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_connection.py +92 -31
  164. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_connection_redaction.py +2 -2
  165. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_constraints.py +6 -7
  166. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_cross_emitter_parity.py +63 -74
  167. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_crud.py +86 -59
  168. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_db_type_cross_emitter_parity.py +173 -15
  169. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_db_type_integration.py +56 -52
  170. ferro_orm-0.14.0/tests/test_deletion.py +90 -0
  171. ferro_orm-0.14.0/tests/test_documentation_features.py +874 -0
  172. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_enum_cold_hydration.py +11 -8
  173. ferro_orm-0.14.0/tests/test_exception_mapping.py +160 -0
  174. ferro_orm-0.14.0/tests/test_exceptions.py +126 -0
  175. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_field_wrapper.py +17 -11
  176. ferro_orm-0.14.0/tests/test_fk_target_pk.py +69 -0
  177. ferro_orm-0.14.0/tests/test_helpers.py +136 -0
  178. ferro_orm-0.14.0/tests/test_hydration.py +204 -0
  179. ferro_orm-0.14.0/tests/test_hydration_equivalence.py +275 -0
  180. ferro_orm-0.14.0/tests/test_identity_memory.py +80 -0
  181. ferro_orm-0.14.0/tests/test_identity_refresh.py +79 -0
  182. ferro_orm-0.14.0/tests/test_identity_scoped_invalidation.py +86 -0
  183. ferro_orm-0.14.0/tests/test_identity_weakref.py +37 -0
  184. ferro_orm-0.14.0/tests/test_import_budget.py +38 -0
  185. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_ir_vectors_contract.py +92 -1
  186. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_metaclass_internals.py +44 -5
  187. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_metadata.py +25 -22
  188. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_migrate_plan.py +99 -14
  189. ferro_orm-0.14.0/tests/test_model_identity.py +234 -0
  190. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_models.py +4 -1
  191. ferro_orm-0.14.0/tests/test_mutation_pagination_guard.py +99 -0
  192. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_named_connections_integration.py +52 -32
  193. ferro_orm-0.14.0/tests/test_naming_single_source.py +315 -0
  194. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_one_to_one.py +22 -20
  195. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_query_builder.py +90 -112
  196. ferro_orm-0.14.0/tests/test_query_column_validation.py +151 -0
  197. ferro_orm-0.14.0/tests/test_query_immutability.py +101 -0
  198. ferro_orm-0.14.0/tests/test_query_typing.py +173 -0
  199. ferro_orm-0.14.0/tests/test_raw_sql.py +528 -0
  200. ferro_orm-0.14.0/tests/test_refresh.py +55 -0
  201. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_relationship_engine.py +10 -2
  202. ferro_orm-0.14.0/tests/test_relationship_resolution_errors.py +47 -0
  203. ferro_orm-0.14.0/tests/test_route_single_site.py +33 -0
  204. ferro_orm-0.14.0/tests/test_routing_errors.py +64 -0
  205. ferro_orm-0.14.0/tests/test_save_pk_edges.py +53 -0
  206. ferro_orm-0.14.0/tests/test_save_semantics.py +358 -0
  207. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_schema.py +6 -6
  208. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_session.py +14 -18
  209. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_shadow_fk_types.py +58 -54
  210. ferro_orm-0.14.0/tests/test_shadow_reports.py +107 -0
  211. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_sqlite_alembic_reconnect_hydration.py +21 -11
  212. ferro_orm-0.14.0/tests/test_static_contracts.py +191 -0
  213. ferro_orm-0.14.0/tests/test_string_search.py +54 -0
  214. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_structural_types.py +152 -140
  215. ferro_orm-0.14.0/tests/test_temporal_types.py +75 -0
  216. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_transactions.py +134 -117
  217. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_typed_null_binds.py +130 -115
  218. ferro_orm-0.14.0/tests/test_typed_save_bind.py +196 -0
  219. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/uv.lock +28 -1
  220. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/zensical.toml +2 -2
  221. ferro_orm-0.12.3/crates/ferro-migrate/src/lib.rs +0 -271
  222. ferro_orm-0.12.3/docs/examples/mutations.py +0 -49
  223. ferro_orm-0.12.3/docs/examples/predicates.py +0 -92
  224. ferro_orm-0.12.3/docs/examples/quickstart.py +0 -111
  225. ferro_orm-0.12.3/docs/examples/raw_sql.py +0 -43
  226. ferro_orm-0.12.3/docs/examples/transactions.py +0 -58
  227. ferro_orm-0.12.3/docs/pages/api/exceptions.md +0 -5
  228. ferro_orm-0.12.3/docs/pages/api/queries.md +0 -11
  229. ferro_orm-0.12.3/docs/pages/concepts/identity-map.md +0 -151
  230. ferro_orm-0.12.3/docs/pages/concepts/query-typing.md +0 -106
  231. ferro_orm-0.12.3/docs/pages/howto/migrating-to-v0-12-0.md +0 -151
  232. ferro_orm-0.12.3/src/codec.rs +0 -517
  233. ferro_orm-0.12.3/src/ferro/_core.pyi +0 -199
  234. ferro_orm-0.12.3/src/ferro/exceptions.py +0 -17
  235. ferro_orm-0.12.3/src/ferro/state.py +0 -166
  236. ferro_orm-0.12.3/src/hydration.rs +0 -57
  237. ferro_orm-0.12.3/src/migrate.rs +0 -1481
  238. ferro_orm-0.12.3/src/operations.rs +0 -3228
  239. ferro_orm-0.12.3/src/query.rs +0 -853
  240. ferro_orm-0.12.3/src/schema.rs +0 -1166
  241. ferro_orm-0.12.3/src/schema_bind.rs +0 -49
  242. ferro_orm-0.12.3/src/state.rs +0 -328
  243. ferro_orm-0.12.3/tests/fixtures/shadow_reports/postgres.json +0 -47
  244. ferro_orm-0.12.3/tests/fixtures/shadow_reports/sqlite.json +0 -47
  245. ferro_orm-0.12.3/tests/test_aggregation.py +0 -70
  246. ferro_orm-0.12.3/tests/test_auto_migrate.py +0 -582
  247. ferro_orm-0.12.3/tests/test_bulk_update.py +0 -62
  248. ferro_orm-0.12.3/tests/test_deletion.py +0 -87
  249. ferro_orm-0.12.3/tests/test_deprecated_operator_inventory.py +0 -51
  250. ferro_orm-0.12.3/tests/test_documentation_features.py +0 -833
  251. ferro_orm-0.12.3/tests/test_framework_predicates.py +0 -72
  252. ferro_orm-0.12.3/tests/test_helpers.py +0 -129
  253. ferro_orm-0.12.3/tests/test_hydration.py +0 -167
  254. ferro_orm-0.12.3/tests/test_query_typing.py +0 -295
  255. ferro_orm-0.12.3/tests/test_raw_sql.py +0 -493
  256. ferro_orm-0.12.3/tests/test_refresh.py +0 -53
  257. ferro_orm-0.12.3/tests/test_shadow_reports.py +0 -112
  258. ferro_orm-0.12.3/tests/test_static_contracts.py +0 -8
  259. ferro_orm-0.12.3/tests/test_string_search.py +0 -52
  260. ferro_orm-0.12.3/tests/test_temporal_types.py +0 -71
  261. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  262. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  263. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/PERMISSIONS.md +0 -0
  264. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/PYPI_CHECKLIST.md +0 -0
  265. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/PYPI_SETUP.md +0 -0
  266. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/generated/wheels.generated.yml +0 -0
  267. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/pull_request_template.md +0 -0
  268. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/workflows/packaging-smoke.yml +0 -0
  269. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/workflows/publish-docs.yml +0 -0
  270. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/workflows/publish.yml +0 -0
  271. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.github/workflows/release.yml +0 -0
  272. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.gitignore +0 -0
  273. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.pre-commit-config.yaml +0 -0
  274. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/.python-version +0 -0
  275. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/CONTRIBUTING.md +0 -0
  276. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/LICENSE +0 -0
  277. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/crates/ferro-schema-ir/Cargo.toml +0 -0
  278. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/brainstorms/2026-04-29-named-connections-role-routing-requirements.md +0 -0
  279. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/brainstorms/2026-05-08-typed-query-predicates-requirements.md +0 -0
  280. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/brainstorms/2026-05-13-configurable-column-storage-types-requirements.md +0 -0
  281. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/brainstorms/2026-05-14-autogenerate-support-for-db-type-requirements.md +0 -0
  282. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/brainstorms/2026-05-25-annotated-strenum-cold-hydration-requirements.md +0 -0
  283. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/connection.md +0 -0
  284. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/fields.md +0 -0
  285. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/model.md +0 -0
  286. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/raw-sql.md +0 -0
  287. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/relationships.md +0 -0
  288. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/api/transactions.md +0 -0
  289. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/changelog.md +0 -0
  290. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/concepts/backends.md +0 -0
  291. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/concepts/performance.md +0 -0
  292. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/contributing.md +0 -0
  293. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/getting-started/installation.md +0 -0
  294. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/getting-started/next-steps.md +0 -0
  295. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/raw-sql.md +0 -0
  296. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/relationships.md +0 -0
  297. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/guide/transactions.md +0 -0
  298. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/index.md +0 -0
  299. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/roadmap.md +0 -0
  300. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/stylesheets/extra.css +0 -0
  301. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/pages/why-ferro.md +0 -0
  302. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-04-24-001-refactor-multi-db-backend-architecture-plan.md +0 -0
  303. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-04-29-001-typed-null-binds-plan.md +0 -0
  304. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-04-29-002-feat-named-connections-plan.md +0 -0
  305. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-05-07-001-refactor-generic-model-connection-plan.md +0 -0
  306. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-05-08-001-feat-typed-query-predicates-plan.md +0 -0
  307. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-05-13-001-feat-configurable-column-storage-types-plan.md +0 -0
  308. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/2026-05-25-001-fix-annotated-strenum-cold-hydration-plan.md +0 -0
  309. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/plans/ir-first-release-checklist.md +0 -0
  310. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/rfc/ir-contracts-v1.md +0 -0
  311. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/README.md +0 -0
  312. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/pydantic-slots-missing-after-ferro-hydration.md +0 -0
  313. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/python-3.14-deferred-annotation-typeerror-swallow.md +0 -0
  314. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/sqlite-integer-decimal-hydrates-as-none.md +0 -0
  315. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/sqlite-null-hydrates-as-int-zero.md +0 -0
  316. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/issues/typed-where-null-panics-is-null.md +0 -0
  317. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/configurable-column-storage-types.md +0 -0
  318. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/ddl-on-live-engine.md +0 -0
  319. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/foreign-key-index.md +0 -0
  320. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/index-unique-redundancy.md +0 -0
  321. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/ir-invariants.md +0 -0
  322. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/shadow-fk-columns.md +0 -0
  323. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/sqlite-alembic-reconnect-hydration-tests.md +0 -0
  324. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/docs/solutions/patterns/typed-null-binds.md +0 -0
  325. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/scripts/demo_queries.py +0 -0
  326. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/scripts/demo_queries_pre_v012.py +0 -0
  327. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/_annotation_utils.py +0 -0
  328. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/_deprecations.py +0 -0
  329. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/_shadow_fk_types.py +0 -0
  330. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/base.py +0 -0
  331. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/composite_indexes.py +0 -0
  332. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/composite_uniques.py +0 -0
  333. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/fields.py +0 -0
  334. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/ir/__init__.py +0 -0
  335. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/migrations/__init__.py +0 -0
  336. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/py.typed +0 -0
  337. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/src/ferro/session.py +0 -0
  338. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/__init__.py +0 -0
  339. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/db_backends.py +0 -0
  340. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/fixtures/ir_vectors/README.md +0 -0
  341. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/fixtures/ir_vectors/codec_registry_core_v1.json +0 -0
  342. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/fixtures/ir_vectors/query_user_compound_v1.json +0 -0
  343. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_alembic_db_type.py +0 -0
  344. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_alembic_nullability.py +0 -0
  345. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_alembic_type_mapping.py +0 -0
  346. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_db_backends.py +0 -0
  347. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_db_type_typing.py +0 -0
  348. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_db_type_validation.py +0 -0
  349. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_deprecations.py +0 -0
  350. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_docs_examples.py +0 -0
  351. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_schema_constraints.py +0 -0
  352. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_schema_db_type_metadata.py +0 -0
  353. {ferro_orm-0.12.3 → ferro_orm-0.14.0}/tests/test_schema_enum_annotations.py +0 -0
@@ -3,9 +3,8 @@ name: CI
3
3
  on:
4
4
  workflow_call:
5
5
  pull_request:
6
- branches: [main, feat/ir-first]
7
6
  push:
8
- branches: [main, feat/ir-first]
7
+ branches: [main]
9
8
  workflow_dispatch:
10
9
 
11
10
  concurrency:
@@ -153,6 +152,10 @@ jobs:
153
152
  run: |
154
153
  uv run maturin develop
155
154
 
155
+ - name: Static type gate (ty, scoped)
156
+ run: |
157
+ uv run ty check src/ferro/query tests/test_query_typing.py tests/test_static_contracts.py
158
+
156
159
  - name: Run IR vector contract harness
157
160
  run: |
158
161
  uv run pytest -v tests/test_ir_vectors_contract.py
@@ -27,18 +27,23 @@ For a single model, every emitter must agree on:
27
27
 
28
28
  1. **Table name** — already handled by `model_name.lower()`.
29
29
  2. **Column names** — including shadow `*_id` columns from `ForeignKey`.
30
- 3. **Column types** — pydantic JSON schema → SQL type mapping must be one
31
- function (or two functions whose outputs are tested for parity). This
32
- includes the canonical `db_type` vocabulary (`text`, `varchar(N)`,
33
- `smallint`, `int`, `bigint`, `uuid`, `timestamp`, `timestamptz`, `date`,
34
- `time`) — duplicated in `_db_type_to_sa_type` (Python) and
35
- `db_type_token_to_canonical` (Rust), pinned by
36
- `tests/test_db_type_cross_emitter_parity.py`.
30
+ 3. **Column types** — decided by ONE function:
31
+ `ferro_ddl_lowering::resolve_column_storage` (explicit `db_type` token →
32
+ native-enum resolution → the `canonical_from_parts` cascade). The Alembic
33
+ bridge consumes it mechanically over FFI (`_core._resolve_storage_type`)
34
+ and `_db_type_to_sa_type` is only the SA *rendering* of the shared token
35
+ vocabulary — never a second decision table. Pinned exhaustively by
36
+ `tests/test_db_type_cross_emitter_parity.py` (every token and every
37
+ derived annotation × both dialects). See
38
+ `docs/solutions/patterns/derived-type-and-naming-decision-table.md`.
37
39
  4. **Index names** — `idx_<table>_<col>` for single-column indexes,
38
40
  `idx_<table>_<col1>_<col2>...` for composite indexes.
39
41
  5. **Unique constraint names** — `uq_<table>_<col>` for single-column,
40
42
  `uq_<table>_<col1>_<col2>...` for composite.
41
- 6. **Foreign key constraint names** — when explicitly named.
43
+ 6. **Foreign key constraint names** — `fk_<table>_<col>_<to_table>`, always
44
+ emitted (both emitters render `SchemaForeignKey.name`; single-sourced in
45
+ `ferro_ddl_lowering::fk_name`). See
46
+ `docs/solutions/patterns/derived-type-and-naming-decision-table.md`.
42
47
  7. **Primary key constraint names** — when explicitly named.
43
48
  8. **Check constraint names** — `ck_<table>_<col>` for the single-column
44
49
  `db_check=True` constraint; generated by `_ck_constraint_name` (Python)
@@ -91,7 +96,7 @@ If you add a new schema feature (e.g. partial indexes, exclusion constraints):
91
96
  2. Implement it in **both** the Alembic and Rust paths in the same PR.
92
97
  3. Add a parity test that asserts the names match.
93
98
  4. Do not edit `CHANGELOG.md` manually — release tooling records entries at
94
- release time (see I-11).
99
+ release time (see I-10).
95
100
 
96
101
  ---
97
102
 
@@ -151,17 +156,7 @@ See `docs/solutions/README.md` for the frontmatter conventions.
151
156
 
152
157
  ---
153
158
 
154
- ## I-6: No AI attribution in commits or PRs
155
-
156
- Never sign commits or pull requests with AI/agent attribution. No
157
- `Co-Authored-By: Claude ...` trailers, no "Generated with Claude Code"
158
- footers, no robot emoji bylines — in commit messages, PR titles, or PR
159
- bodies. This applies even when an agent's default behavior is to add them:
160
- this rule overrides those defaults for this repository.
161
-
162
- ---
163
-
164
- ## I-7: No stop-gap solutions
159
+ ## I-6: No stop-gap solutions
165
160
 
166
161
  Every feature, bug fix, and improvement must be designed as the best,
167
162
  well-thought-out solution for the project with the library's future in
@@ -194,7 +189,7 @@ agent default that biases toward minimal or expedient changes.
194
189
 
195
190
  ---
196
191
 
197
- ## I-8: Docs examples show both field-declaration styles
192
+ ## I-7: Docs examples show both field-declaration styles
198
193
 
199
194
  Ferro supports two equivalent ways to declare model fields: assignment
200
195
  (`name: str = Field(unique=True)`) and `Annotated` metadata
@@ -232,18 +227,20 @@ preferred declaration style.
232
227
 
233
228
  ---
234
229
 
235
- ## I-9: Lambda predicates are the official query style
230
+ ## I-8: Lambda predicates are the official query style
236
231
 
237
232
  Documentation and examples use the lambda predicate style for all queries:
238
233
 
239
234
  ```python
240
- adults = await User.where(lambda t: t.age >= 18).all()
235
+ adults = await User.where(lambda user: user.age >= 18).all()
241
236
  ```
242
237
 
243
238
  Rules:
244
239
 
245
240
  - **Every query example** in docs, docstring `Examples:` sections, and
246
241
  `docs/examples/` scripts uses lambda predicates.
242
+ - Name the lambda parameter after the model in **lowercase singular**
243
+ (`user` for `User`, `post` for `Post`) so filters read naturally.
247
244
  - When the predicate styles themselves are documented, present them in
248
245
  order **lambda > `col()` > operator**, with lambda labeled the officially
249
246
  recommended style.
@@ -262,7 +259,7 @@ else uses lambda without restating the trade-offs.
262
259
 
263
260
  ---
264
261
 
265
- ## I-10: PRs must close scoped issues explicitly
262
+ ## I-9: PRs must close scoped issues explicitly
266
263
 
267
264
  Issue closure is part of feature completion, not optional cleanup.
268
265
 
@@ -280,7 +277,7 @@ Rules:
280
277
 
281
278
  ---
282
279
 
283
- ## I-11: Do not edit CHANGELOG.md manually
280
+ ## I-10: Do not edit CHANGELOG.md manually
284
281
 
285
282
  `CHANGELOG.md` is updated automatically by the release workflow (semantic
286
283
  release / release tooling). Agents and contributors must **not** add,
@@ -295,3 +292,29 @@ What to do instead:
295
292
  If release tooling fails to capture a change, fix the release configuration or
296
293
  commit message conventions — do not patch `CHANGELOG.md` by hand in ordinary
297
294
  PRs.
295
+
296
+ ---
297
+
298
+ ## I-11: Explain technical concepts in plain language, example-first
299
+
300
+ When explaining a concept, design, trade-off, or change to the maintainer, lead
301
+ with plain language and a concrete, user-facing example — not internal function
302
+ names and call graphs.
303
+
304
+ Rules:
305
+
306
+ - **Anchor on what the user sees.** Show a real model definition and the SQL (or
307
+ behavior) it produces, then explain the internal mechanics against that
308
+ anchor. "A `datetime` field becomes `timestamptz` — here's the `CREATE TABLE`"
309
+ beats "`canonical_from_parts` maps the logical type."
310
+ - **Introduce jargon only after the plain version.** Function and type names are
311
+ precise back-references once the idea is clear — not the primary explanation.
312
+ - **Use analogies for architecture.** "Two translation dictionaries that must
313
+ agree by hand" communicates a duplication smell faster than a module diagram.
314
+ - **Show, don't just tell.** Prefer before/after diffs, rendered SQL, and
315
+ concrete values over abstract prose (complements the show-don't-tell habit
316
+ used in issue/PR explanations).
317
+
318
+ This applies to brainstorming, design discussions, PR descriptions, issue
319
+ comments, and any explanation directed at the maintainer. It governs how work is
320
+ communicated, not what gets built.
@@ -1,6 +1,193 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## v0.14.0 (2026-07-07)
5
+
6
+ ### Bug Fixes
7
+
8
+ - **ff-g**: Make Postgres db_check ADD CONSTRAINT idempotent (G6, #176)
9
+ ([#181](https://github.com/syn54x/ferro-orm/pull/181),
10
+ [`2c0e4cf`](https://github.com/syn54x/ferro-orm/commit/2c0e4cf6c922a73454198f6aa0aa75b30eb1f0c8))
11
+
12
+ ### Chores
13
+
14
+ - **benchmarks**: Pinned async benchmark suite over the rich-type hot path
15
+ ([#196](https://github.com/syn54x/ferro-orm/pull/196),
16
+ [`d9a656b`](https://github.com/syn54x/ferro-orm/commit/d9a656b320bd7fb6b613f4408a796161e9badc41))
17
+
18
+ ### Documentation
19
+
20
+ - Consolidate migration guides into one evergreen upgrade guide
21
+ ([#234](https://github.com/syn54x/ferro-orm/pull/234),
22
+ [`15c83db`](https://github.com/syn54x/ferro-orm/commit/15c83dbc6b1c057a78dfc425d70441e50c359f5d))
23
+
24
+ - Fixes roadmap
25
+ ([`5718d02`](https://github.com/syn54x/ferro-orm/commit/5718d02630b6c84d3ced6f8a3080adf4e7fe2383))
26
+
27
+ - **fable-fixes**: Fold #176 into Epic FF-G as sub-task G6
28
+ ([`b966e65`](https://github.com/syn54x/ferro-orm/commit/b966e65739d7c33ed666a7fd14086a27688516dd))
29
+
30
+ - **ff-a**: A5 — docs & migration guide for the mutation-surface changes
31
+ ([#180](https://github.com/syn54x/ferro-orm/pull/180),
32
+ [`ffc5476`](https://github.com/syn54x/ferro-orm/commit/ffc5476f9ba72f03a35048f283b11390f813856b))
33
+
34
+ - **ff-b**: Tick FF-B sub-task and exit-gate boxes in the fable-fixes roadmap
35
+ ([`29f9172`](https://github.com/syn54x/ferro-orm/commit/29f91724e2b6886f8299bbb42940c804c8e88141))
36
+
37
+ ### Features
38
+
39
+ - **ff-a**: Create() is a real INSERT; save() distinguishes INSERT from UPDATE (A3+A4)
40
+ ([#179](https://github.com/syn54x/ferro-orm/pull/179),
41
+ [`de3ec30`](https://github.com/syn54x/ferro-orm/commit/de3ec30196202ee14c42afc5b02f163f82a68451))
42
+
43
+ - **ff-a**: Reject limit/offset on mutating queries
44
+ ([#178](https://github.com/syn54x/ferro-orm/pull/178),
45
+ [`67faf42`](https://github.com/syn54x/ferro-orm/commit/67faf421d49984c5be16d2163982f13ab86cc5e4))
46
+
47
+ - **ff-a**: Typed DBAPI-shaped exception hierarchy mapped from sqlx errors
48
+ ([#177](https://github.com/syn54x/ferro-orm/pull/177),
49
+ [`9c306c5`](https://github.com/syn54x/ferro-orm/commit/9c306c549e716dd4a5158d655265aa7890e63c0b))
50
+
51
+ - **ff-b**: B1 canonical derived-type & naming decision table + refusal-rail scaffolding
52
+ ([`8c879f7`](https://github.com/syn54x/ferro-orm/commit/8c879f77833a7e5a983d53012efc8332cca5b852))
53
+
54
+ - **ff-b**: B2+B6 one derived-type decision table; native PG enums + timestamptz/time parity; delete
55
+ bridge mirrors
56
+ ([`bfdc1fd`](https://github.com/syn54x/ferro-orm/commit/bfdc1fda1eceebe70ae711e38b785af88b46e11e))
57
+
58
+ - **ff-b**: B3+B4 single-source artifact naming; both emitters emit named fk_/uq_ artifacts
59
+ ([`6b771f3`](https://github.com/syn54x/ferro-orm/commit/6b771f38933c72e8f848013a5ae93708fabf5c7a))
60
+
61
+ - **ff-c**: C1 — per-model ColumnCodec plan; delete codec.rs schema sniffing (F5)
62
+ ([#197](https://github.com/syn54x/ferro-orm/pull/197),
63
+ [`0e8e572`](https://github.com/syn54x/ferro-orm/commit/0e8e57252f4cca2d2bc9fffd06d9155338056cf3))
64
+
65
+ - **ff-c**: C2 — schema-epoch catalog cache; zero catalog queries on steady-state CRUD
66
+ ([#200](https://github.com/syn54x/ferro-orm/pull/200),
67
+ [`8c6d6ed`](https://github.com/syn54x/ferro-orm/commit/8c6d6edf578cf55ed7ada0d49359a7e09528da1e))
68
+
69
+ - **ff-c**: C3+C4 — native typed Postgres decode; plan-driven enum hydration replaces _fix_types
70
+ ([#198](https://github.com/syn54x/ferro-orm/pull/198),
71
+ [`05a008c`](https://github.com/syn54x/ferro-orm/commit/05a008cb73f6c4dc8b2478a441c99282d07bd41d))
72
+
73
+ - **ff-d**: Session-scoped weak identity map with refresh-on-load; single-handle routing
74
+ ([#201](https://github.com/syn54x/ferro-orm/pull/201),
75
+ [`a328cfc`](https://github.com/syn54x/ferro-orm/commit/a328cfcd107e2cac998ddc8af50845857ec669a5))
76
+
77
+ - **ff-e**: Registry & model identity — qualified keys, configurable tables, O(N) import
78
+ ([#209](https://github.com/syn54x/ferro-orm/pull/209),
79
+ [`6391805`](https://github.com/syn54x/ferro-orm/commit/6391805c1b4ee46c0590513ac6684237c18f7430))
80
+
81
+ - **ff-f**: Query builder 1.0 shape — immutable chaining, build-time column validation, lambda-only
82
+ predicates, QueryIR-only Rust ([#223](https://github.com/syn54x/ferro-orm/pull/223),
83
+ [`eb7fece`](https://github.com/syn54x/ferro-orm/commit/eb7fece8fbc2231220ecf9111616f803f2f987eb))
84
+
85
+ - **ff-g-a**: Hardening — hydration ABI guard, transactional PG migrate, correctness edges,
86
+ decode-path caching ([#232](https://github.com/syn54x/ferro-orm/pull/232),
87
+ [`22617e1`](https://github.com/syn54x/ferro-orm/commit/22617e1cfe5e2bc9d0a3d7a1fed5acf4d8589cef))
88
+
89
+ ### Refactoring
90
+
91
+ - **ff-g-b**: Operations.rs dedup — ModelMeta + Executor (G2)
92
+ ([#233](https://github.com/syn54x/ferro-orm/pull/233),
93
+ [`09c2c62`](https://github.com/syn54x/ferro-orm/commit/09c2c6214af9e2f9510d39d037935c53bd2e796d))
94
+
95
+ ### Testing
96
+
97
+ - **ff-b**: B5 I-1 sentinel on the full backend matrix with a full-type fixture, zero filters
98
+ ([`1141eb4`](https://github.com/syn54x/ferro-orm/commit/1141eb437b4843303e01556a5b24ddee9d0e0279))
99
+
100
+ - **ff-b**: Force psycopg v3 driver in the Postgres sentinel regardless of URL scheme
101
+ ([`64f9845`](https://github.com/syn54x/ferro-orm/commit/64f98454bdb5e090a41bd214de06b7670974f4eb))
102
+
103
+
104
+ ## v0.13.0 (2026-07-02)
105
+
106
+ ### Bug Fixes
107
+
108
+ - **ir-p8.6**: Datetime/timestamptz coarseness — stop silent Postgres column reinterpretation (#154)
109
+ ([#167](https://github.com/syn54x/ferro-orm/pull/167),
110
+ [`7182a57`](https://github.com/syn54x/ferro-orm/commit/7182a5744900abe7ed50915edbca7d5184b2c535))
111
+
112
+ - **ir-p8.6**: Stop false-positive BLOB drift warning on SQLite (#165)
113
+ ([#168](https://github.com/syn54x/ferro-orm/pull/168),
114
+ [`d554ac4`](https://github.com/syn54x/ferro-orm/commit/d554ac4558286106bcb0908baec9fa3d49a2781d))
115
+
116
+ - **ir-p8.6**: Surface real PEP 649 deferred-annotation error (#155)
117
+ ([#163](https://github.com/syn54x/ferro-orm/pull/163),
118
+ [`00976e7`](https://github.com/syn54x/ferro-orm/commit/00976e748d86246b8be47230229d19516d8bb3c0))
119
+
120
+ ### Documentation
121
+
122
+ - Use model-named lambda predicates in examples
123
+ ([#132](https://github.com/syn54x/ferro-orm/pull/132),
124
+ [`420ca6e`](https://github.com/syn54x/ferro-orm/commit/420ca6e1cb4a90d1acc556a83373c9da99b22bb3))
125
+
126
+ - **agents**: Add I-11 — explain concepts plainly, example-first
127
+ ([`efc7ef5`](https://github.com/syn54x/ferro-orm/commit/efc7ef5678e2f6efacaa3a6e84c0575b0cdef51c))
128
+
129
+ - **ir-first**: Add Phase 8.6 post-8.5 cleanup backlog (epic #145)
130
+ ([#147](https://github.com/syn54x/ferro-orm/pull/147),
131
+ [`e57b74b`](https://github.com/syn54x/ferro-orm/commit/e57b74b397bd51ebc4809f25d57536896540b9de))
132
+
133
+ - **ir-first**: Expand #144 scope — auto-migrate index/unique reconciliation
134
+ ([#150](https://github.com/syn54x/ferro-orm/pull/150),
135
+ [`fdbac40`](https://github.com/syn54x/ferro-orm/commit/fdbac4003e9bd25fdc3f3cb0012c5dd9d562ee68))
136
+
137
+ - **ir-first**: Lowering-consolidation audit + Phase 8.5
138
+ ([#138](https://github.com/syn54x/ferro-orm/pull/138),
139
+ [`c6ce53c`](https://github.com/syn54x/ferro-orm/commit/c6ce53c2409a1fd20a40c1de58d42e1e3d2e1344))
140
+
141
+ - **ir-p8.6**: Add #153 create-path IR unification spec
142
+ ([`be4bb99`](https://github.com/syn54x/ferro-orm/commit/be4bb99b90e6d02a6e01bfc5807a7cb178055862))
143
+
144
+ - **ir-p8.6**: Add #153 create-path unification implementation plan
145
+ ([`de3c427`](https://github.com/syn54x/ferro-orm/commit/de3c42794de821d8e47eb5ca94b582f8199992df))
146
+
147
+ - **ir-p8.6**: Sync roadmap — #153 merged, file #158 (db_check check-renderer)
148
+ ([`62c80fa`](https://github.com/syn54x/ferro-orm/commit/62c80fa3fe7ac456169d280f3ee8795feac32a8c))
149
+
150
+ - **ir-p8.6**: Sync roadmap — #154 merged (datetime/timestamptz warn-and-skip); #145 now 6/7
151
+ ([`31dbbb6`](https://github.com/syn54x/ferro-orm/commit/31dbbb6c0f1d3afa0ed452ed9ae34441df69c501))
152
+
153
+ - **ir-p8.6**: Sync roadmap — #162 merged (typed save/update bind), file #165
154
+ ([`5ca76a1`](https://github.com/syn54x/ferro-orm/commit/5ca76a1e177f4847ded4fb19402841a9988b62d1))
155
+
156
+ - **ir-p8.6**: Sync roadmap — #165 merged (BLOB introspection); #145 complete 7/7, Phase 8.6 wrapped
157
+ ([`1dbb278`](https://github.com/syn54x/ferro-orm/commit/1dbb27864881d77a68d5026ab5a9bb07f142c3c5))
158
+
159
+ - **rust**: Add detailed docstrings for all public APIs
160
+ ([#130](https://github.com/syn54x/ferro-orm/pull/130),
161
+ [`8d5a951`](https://github.com/syn54x/ferro-orm/commit/8d5a95162568eef215ba03524a31b657df87e8c6))
162
+
163
+ ### Features
164
+
165
+ - Ir cutover ([#137](https://github.com/syn54x/ferro-orm/pull/137),
166
+ [`0509c2a`](https://github.com/syn54x/ferro-orm/commit/0509c2a35b0f0200492143a52563146359550129))
167
+
168
+ - **ir-p8.6**: Route save/update/bulk value bind through the typed codec path (#162)
169
+ ([#166](https://github.com/syn54x/ferro-orm/pull/166),
170
+ [`486e68c`](https://github.com/syn54x/ferro-orm/commit/486e68c110c03356fa579051849b44e8f4eae751))
171
+
172
+ ### Refactoring
173
+
174
+ - Lowering consolidation & single-source-of-truth closeout (#139)
175
+ ([#156](https://github.com/syn54x/ferro-orm/pull/156),
176
+ [`f76bec9`](https://github.com/syn54x/ferro-orm/commit/f76bec93651bc9741bea85171da927ddb8864a1a))
177
+
178
+ - Unify the CREATE TABLE path onto the Python SchemaIR (#153)
179
+ ([#157](https://github.com/syn54x/ferro-orm/pull/157),
180
+ [`c9ce7b0`](https://github.com/syn54x/ferro-orm/commit/c9ce7b093fde350da8a1653cf7c42f522e0523b2))
181
+
182
+ - Unify the three dialect enums into one shared Dialect (#146)
183
+ ([#159](https://github.com/syn54x/ferro-orm/pull/159),
184
+ [`4b0dfe6`](https://github.com/syn54x/ferro-orm/commit/4b0dfe6b9eca1a2a63631a54504c629f42f7c5e1))
185
+
186
+ - **ir-p8.6**: Single check-renderer for db_check (#158)
187
+ ([#161](https://github.com/syn54x/ferro-orm/pull/161),
188
+ [`c04df1c`](https://github.com/syn54x/ferro-orm/commit/c04df1ce5aa4505f737864944855ff4170a4d350))
189
+
190
+
4
191
  ## v0.12.3 (2026-06-24)
5
192
 
6
193
  ### Bug Fixes