# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.37.2]

### Fixed

- Module fakes (`Double.fake(contract, Module)`) now run in the
  calling process instead of the NimbleOwnership GenServer process.
  Previously `invoke_module_fallback` called `apply(module, op, args)`
  directly inside `get_and_update`, which meant real implementations
  doing I/O (e.g. Ecto queries) ran in the GenServer — a process
  with no Ecto sandbox checkout. Now uses `%Defer{}` to move the
  `apply` outside the lock, matching how `transact` already works.
  This fixes `DBConnection.OwnershipError` when using
  `Double.fake(Repo, Backend.Repo)` in integration tests.

## [0.37.1]

### Added

- `DoubleDown.Double.allow/2,3` — convenience delegate to
  `DoubleDown.Testing.allow/2,3` for discoverability when using the
  Double API exclusively.
- Documented `:warn_on_typespec_mismatch?` option in `defcallback`
  `@doc`.

### Fixed

- Moved `DoubleDown.Defer` to `DoubleDown.Dispatch.Defer` — it's an
  internal dispatch mechanism, not user-facing.
- `invoke_stateful_fallback` now validates return tuple shape
  consistently with `invoke_expect` and `invoke_stub`. A stateful
  fake returning a bare value instead of `{result, new_state}` now
  raises a descriptive `ArgumentError` instead of a raw `MatchError`.
- `stub_handler?/1` and `fake_handler?/1` now check `@behaviour`
  declarations instead of duck-typing function exports. Previously
  any module with `new/2` would match `stub_handler?`.
- `Testing.allow/2` now has an explicit `@spec` (was missing for the
  2-arity form generated by the default argument).
- `verify!` error message no longer incorrectly says `verify!/0`
  when called via `verify!/1`.

## [0.37.0]

### Added

- `DoubleDown.Dispatch.StubHandler` behaviour for stateless stub
  handler modules. Implement `new/2` to make a stub usable by module
  name in `Double.stub`:

      Double.stub(Repo, Repo.Test)
      Double.stub(Repo, Repo.Test, fn :all, [User] -> [] end)

- `Repo.Test` implements `StubHandler`. `new/2` accepts a fallback
  function as the first arg and opts as the second. The legacy
  `new(fallback_fn: fn ...)` keyword form is still supported.
- `Double.stub/2` auto-detects StubHandler modules. `Double.stub/3`
  disambiguates StubHandler modules from per-operation stubs by
  checking if the second arg is a loaded module implementing the
  behaviour.

## [0.36.0]

### Added

- `DoubleDown.Dispatch.FakeHandler` behaviour for stateful fake
  handler modules. Implement `new/2` and `dispatch/3` (or `/4`) to
  make a fake usable by module name in `Double.fake`:

      Double.fake(Repo, Repo.InMemory)
      Double.fake(Repo, Repo.InMemory, [%User{id: 1}])
      Double.fake(Repo, Repo.InMemory, [%User{id: 1}], fallback_fn: fn ...)

- `Repo.InMemory` implements `FakeHandler`. `new/2` accepts seed
  data as the first arg (list of structs or pre-built store map)
  and opts as the second. The legacy `new(seed: [...], fallback_fn: ...)`
  keyword form is still supported.
- `Double.fake/2` auto-detects FakeHandler modules — if the module
  implements the behaviour, it's used as a stateful fake with default
  state. Non-FakeHandler modules are still treated as module fakes.

## [0.35.0]

### Added

- Stateful per-operation stubs. `DoubleDown.Double.stub/3` now accepts
  2-arity and 3-arity responder functions that can read and update the
  fake's state, with the same semantics as stateful expect responders.
  All arities can return `Double.passthrough()` to conditionally
  delegate to the fallback/fake. This enables the pattern "intercept
  every call, decide per-call whether to handle or delegate, without
  knowing the call count."

## [0.34.0]

### Added

- `DoubleDown.Double.passthrough/0` — returns a sentinel value that
  expect responders can return to conditionally delegate to the
  fallback/fake. The expect is still consumed for `verify!` counting.
  This enables patterns like "fail if duplicate, otherwise let the
  fake handle it" without duplicating the fake's logic. Works with
  all responder arities (1, 2, 3).

## [0.33.0]

### Added

- Stateful expect responders. `DoubleDown.Double.expect` now accepts
  2-arity and 3-arity responder functions that can read and update the
  stateful fake's state:
  - **2-arity:** `fn [args], state -> {result, new_state} end`
  - **3-arity:** `fn [args], state, all_states -> {result, new_state} end`
  (cross-contract state access)
  
  Stateful responders require `Double.fake/3` to be called first.
  `ArgumentError` is raised at `expect` time if no stateful fake is
  configured, and at dispatch time if the responder doesn't return a
  `{result, new_state}` tuple. 1-arity expects are unchanged.

### Fixed

- Removed stale "Limitation: no inline passthrough" notes from
  `DoubleDown.Double` moduledoc and `docs/testing.md` — this
  limitation no longer exists with stateful expect responders.
- Fixed historical `.Port.` module name references in docs.
- Removed stale `Skuld` reference in contract.ex comment.

## [0.32.0]

### Added

- 4-arity stateful handlers with read-only cross-contract state
  access. Handlers registered with
  `fn operation, args, state, all_states -> {result, new_state} end`
  receive a snapshot of all contract states as the 4th argument. This
  enables the "two-contract" pattern where a Queries handler reads
  the Repo InMemory store. Works with both `DoubleDown.Double.fake/3`
  and `DoubleDown.Testing.set_stateful_handler/3`. Existing 3-arity
  handlers are unchanged (non-breaking).
- `DoubleDown.Contract.GlobalState` sentinel key in the global state
  map. If a handler accidentally returns the global map instead of
  its own state, a clear `ArgumentError` is raised.

### Fixed

- Exceptions inside stateful handlers no longer crash the
  NimbleOwnership GenServer. Raises, throws, and exits that occur
  inside `NimbleOwnership.get_and_update` are now caught and
  transported to the calling process via `%Defer{}`, where they
  re-raise safely. Previously these would crash the ownership
  server — a singleton for the entire test run — aborting the suite.

## [0.31.1]

### Fixed

- Exceptions inside stateful handlers no longer crash the
  NimbleOwnership GenServer. Raises, throws, and exits that occur
  inside `NimbleOwnership.get_and_update` (e.g. a module fallback
  hitting a dead Ecto sandbox connection during test teardown) are
  now caught and transported to the calling process via `%Defer{}`,
  where they re-raise safely. Previously these would crash the
  ownership server — a singleton for the entire test run — aborting
  the suite.

## [0.31.0]

### Added

- Compile-time spec mismatch detection between `defcallback` type specs
  and the production implementation's `@spec` declarations. When a
  facade is compiled with a known static impl, param types and return
  types are compared and a `CompileError` is raised on mismatch. This
  catches the class of bug where a `defcallback` declares a narrower
  type than the impl accepts (e.g. `keyword()` vs `list()`), which
  would otherwise only surface as a non-local Dialyzer error.
- `warn_on_typespec_mismatch?: true` option on `defcallback` to
  downgrade the compile error to a warning for individual operations
  during migration.
- `DoubleDown.Contract.SpecWarnings` — private module handling spec
  fetching, type AST normalization, and comparison.

## [0.30.1]

### Fixed

- `transact` return type spec now includes the `Ecto.Multi` 4-tuple
  error shape: `{:error, term(), term(), term()}`. Previously the
  spec only declared `{:ok, term()} | {:error, term()}`, causing
  Dialyzer to conclude that code handling Multi's
  `{:error, failed_op, failed_value, changes_so_far}` return was
  unreachable.

## [0.30.0]

### Added

- Opts-accepting variants for all `DoubleDown.Repo` contract operations.
  Every operation now has both a base arity and an `+ opts` arity
  (e.g. `insert/1` and `insert/2`, `get/2` and `get/3`), matching
  `Ecto.Repo`'s actual API where every function accepts an optional
  `opts` keyword list. This fixes `UndefinedFunctionError` when
  `Ecto.Multi.update/4` (and `insert/4`, `delete/4`) receive a
  function argument — Multi's internal `:run` callbacks call
  `repo.update(changeset, opts)` with 2 args, which previously had
  no matching facade function.
- `Repo.Test` and `Repo.InMemory` handle opts-accepting dispatches
  by stripping opts and delegating to base-arity logic.

## [0.29.0]

### Added

- `get_by`/`get_by!` in `Repo.InMemory` now use 3-stage dispatch
  (state → fallback → error) when the queryable is a bare schema
  module and the clauses include all primary key fields. PK lookup
  uses the existing store index — no scan required. If found, any
  additional non-PK clauses are verified against the record. If not
  found in state, falls through to the fallback function (absence is
  not authoritative). Non-PK clauses, `Ecto.Query` queryables, and
  partial composite PKs still delegate to the fallback as before.
- Composite PK support in `get_by`/`get_by!` — all PK fields must
  be present in the clauses for a direct state lookup.

## [0.28.1]

### Changed

- `defcallback` macro `@doc` now includes full rationale for why
  `defcallback` is used instead of plain `@callback` (parameter names,
  combined contract+facade, LSP docs, additional metadata).
- `repo.md`: rollback section, operation dispatch table updated,
  `{:defer, fn}` references updated to `%DoubleDown.Defer{}`.
- `DoubleDown.Contract` `@moduledoc`: "typed port contracts" →
  "contract behaviours".

## [0.28.0]

### Added

- `rollback/1` added to `DoubleDown.Repo` contract (now 17 operations).
  Throws `{:rollback, value}` via `%Defer{}`, caught by `transact`
  which returns `{:error, value}`. Matches `Ecto.Repo.rollback/1` API.
  Both `Repo.Test` and `Repo.InMemory` support rollback — state
  mutations from earlier operations are not undone (documented
  limitation).
- Nested transact tests for both `Repo.Test` and `Repo.InMemory`,
  including via `Double.stub` and `Double.fake`.

## [0.27.0]

### Added

- `%DoubleDown.Defer{fn: fun}` struct — dedicated deferred execution
  marker, replacing the `{:defer, fn}` tuple convention. Eliminates
  clash risk with legitimate return values and enables deferred
  execution in all dispatch paths (fn, module, stateful).
- `Repo.Test` now returns `%Defer{}` for `transact` operations, so
  `Double.stub(contract, Repo.Test.new())` works correctly with
  transact — no NimbleOwnership deadlock.
- Regression tests for transact-via-`Double.stub` scenario.

### Changed

- **Breaking:** `{:defer, fn}` tuple convention replaced by
  `%DoubleDown.Defer{fn: fun}` throughout. Affects `Repo.Test`,
  `Repo.InMemory`, `DoubleDown.Dispatch`, and `DoubleDown.Double`.
  Only relevant if you were returning `{:defer, fn}` from custom
  stateful handlers — replace with `%DoubleDown.Defer{fn: fun}`.

### Fixed

- NimbleOwnership deadlock when using `Double.stub(contract,
  Repo.Test.new())` with contracts that include re-entrant operations
  like `transact`.
- Async test race condition: added `Code.ensure_loaded` before
  `function_exported?` in contract tests.
- Documentation: "contract behaviour" and "dispatch facade" compound
  forms at first-mention points, intro paragraphs on all doc pages,
  production Repo as zero-cost passthrough.

## [0.26.0]

### Changed

- **Breaking:** `DoubleDown.Handler` renamed to `DoubleDown.Double`.
  `stub` for module and stateful fallbacks split out into `fake`:
  - `Double.stub(contract, :op, fun)` — per-operation stub (canned value)
  - `Double.stub(contract, fun)` — 2-arity function fallback
  - `Double.fake(contract, module)` — module fake
  - `Double.fake(contract, fun, init_state)` — stateful fake
- **Breaking:** `DoubleDown.Log` API simplified — `match` and `reject`
  no longer take a contract parameter. The contract is specified once
  at `verify!` time. `verify!` now returns `{:ok, log}` on success.
- Handler error messages now include the contract name and args.
- `.formatter.exs` updated for `defcallback` rename.

### Added

- `DoubleDown.Log.verify!` returns `{:ok, log}` on success and
  includes the full dispatch log in all error messages — useful for
  REPL debugging.
- `:static_dispatch?` option on `use DoubleDown.Facade` — resolves
  the implementation module at compile time and generates direct
  function calls, eliminating `Application.get_env` overhead entirely.
  Defaults to `fn -> Mix.env() == :prod end`.
- Comprehensive docs review: restructured testing.md with `Double` as
  primary API, updated all examples to use `Double.expect`/`stub`/`fake`
  instead of raw `set_*_handler` APIs, consistent terminology throughout.

## [0.25.0]

### Changed

- **Breaking:** `defport` renamed to `defcallback`, `__port_operations__/0`
  renamed to `__callbacks__/0`. The `defcallback` macro uses the same
  syntax as `@callback` — replace the keyword and you're done.
- **Breaking:** `DoubleDown.Repo.Contract` renamed to `DoubleDown.Repo`.
  Less verbose in `Handler.stub` and `Handler.expect` calls.
- **Breaking:** `DoubleDown.Log` API simplified — `match` and `reject`
  no longer take a contract parameter. The contract is specified once
  at `verify!` time: `Log.match(:op, fn _ -> true end) |> Log.verify!(MyContract)`.

### Added

- `:static_dispatch?` option on `use DoubleDown.Facade` — resolves
  the implementation module at compile time and generates direct
  function calls, eliminating `Application.get_env` overhead entirely.
  Defaults to `fn -> Mix.env() == :prod end`. Falls back to runtime
  config dispatch when compile-time config is unavailable.
- README rewritten with new "Why DoubleDown?" section, Mox comparison,
  failure scenario example, and implementation snippet.
- Comprehensive docs review: "port" → "contract" throughout,
  terminology updated, fail-fast pattern documented, Skuld references
  simplified, LSP docs bullet added to `defcallback` rationale.

## [0.24.0]

### Changed

- **Breaking:** Library renamed from `hex_port` / `HexPort` to
  `double_down` / `DoubleDown`. All module names, app name, package
  name, and GitHub URLs updated. The emphasis has shifted from
  hexagonal architecture boundaries to the distinctive test double
  capabilities.

## [0.23.0]

### Changed

- **Breaking:** `DoubleDown.Double` API simplified — `expect` and `stub`
  now write directly to NimbleOwnership with immediate effect. Removed
  `%DoubleDown.Double{}` struct, `new/0`, and `install!/1`. All functions
  return the contract module atom for Mimic-style piping:

      MyContract
      |> DoubleDown.Double.stub(MyImpl)
      |> DoubleDown.Double.expect(:get, fn [id] -> %Thing{id: id} end)

  A canonical handler function is installed on first touch and reads
  dispatch config from state — no builder assembly step needed.

## [0.22.0]

### Added

- `DoubleDown.Double.expect/4..5` now accepts `:passthrough` as the
  handler argument. A `:passthrough` expect delegates to the
  configured fallback (fn, stateful, or module) while consuming the
  expect for `verify!` counting. Supports `times: n`. Enables
  call-counting without changing behaviour, and can be mixed with
  function expects for patterns like "first insert succeeds through
  InMemory, second fails".
- Documentation in `docs/repo.md` for using `DoubleDown.Double` with
  `Repo.Test` and `Repo.InMemory` for failure scenario testing,
  including error simulation, `:passthrough` call counting, and
  combined Handler + Log assertions.

### Fixed

- Added `@spec` clauses for all `stub/2..4` forms to satisfy
  Dialyzer.

## [0.21.0]

### Added

- `DoubleDown.Double.stub/3` (with accumulator: `stub/4`) for module
  fallback — delegates unhandled operations to a module implementing
  the contract's `@behaviour`. Validated at `install!` time.
- `DoubleDown.Double.stub/3` (with accumulator: `stub/4`) for stateful
  fallback — accepts a 3-arity `fn operation, args, state ->
  {result, new_state} end` with initial state, same signature as
  `set_stateful_handler`. Integrates stateful fakes (e.g.
  `Repo.InMemory`) into the Handler dispatch chain. Expects that
  short-circuit (e.g. error simulation) leave the fallback state
  unchanged.
- Fallback types are now a tagged union (`{:fn, fun}`,
  `{:stateful, fun, init_state}`, `{:module, module}`) — mutually
  exclusive, setting one replaces the other.

## [0.20.0]

### Added

- `DoubleDown.Double.verify_on_exit!/0` — registers an `on_exit`
  callback that automatically verifies all expectations after each
  test. Usable as `setup :verify_on_exit!`. Uses
  `NimbleOwnership.set_owner_to_manual_cleanup/2` to preserve
  ownership data until the on_exit callback runs.
- `DoubleDown.Double.verify!/1` — verifies expectations for a
  specific process pid, used internally by `verify_on_exit!/0`.

### Fixed

- Added `:ex_unit` to `plt_add_apps` in `mix.exs` so Dialyzer can
  resolve the `ExUnit.Callbacks.on_exit/2` call in
  `DoubleDown.Double.verify_on_exit!/0`.

## [0.19.0]

### Added

- `DoubleDown.Double.stub/2` and `stub/3` (with accumulator) for
  2-arity contract-wide fallback stubs. Accepts
  `fn operation, args -> result end` — the same signature as
  `set_fn_handler` — as a catch-all for operations without a
  specific expect or per-operation stub. Dispatch priority:
  expects > per-operation stubs > fallback stub > raise.

## [0.18.0]

### Added

- `DoubleDown.Double` — Mox-style expect/stub handler builder. Builds
  stateful handler functions from a declarative specification with
  multi-contract chaining and ordered expectations. API:
  `expect/3..5`, `stub/3..4`, `install!/1`, `verify!/0`.
- `DoubleDown.Log` — log-based expectation matcher. Declares structured
  expectations against the dispatch log after execution, matching on
  the full `{contract, operation, args, result}` tuple. Supports
  loose (default) and strict matching modes, `times: n` counting,
  and `reject` expectations. API: `match/3..5`, `reject/2..3`,
  `verify!/1..2`.
- Terminology mapping and glossary in README and getting-started
  guide, mapping DoubleDown concepts (contract, facade, test double,
  port) to familiar Elixir/Mox equivalents with a stub/mock/fake
  breakdown.

## [0.17.0]

### Changed

- **Breaking:** Renamed generated key helper from `key/N` to `__key__/N`
  on facade modules, following the Elixir convention for generated
  introspection functions. This avoids clashes with user-defined
  `defcallback key(...)` operations.

### Fixed

- Added `:mix` to `plt_add_apps` in `mix.exs` so Dialyzer can resolve
  the compile-time `Mix.env/0` call in `DoubleDown.Facade.__using__/1`.

## [0.16.1]

### Fixed

- Added `:mix` to `plt_add_apps` in `mix.exs` so Dialyzer can resolve
  the compile-time `Mix.env/0` call in `DoubleDown.Facade.__using__/1`.

### Changed

- Documentation updates for `:test_dispatch?` in `docs/getting-started.md`
  (dispatch resolution section) and `docs/testing.md` (setup section).

## [0.16.0]

### Added

- `:test_dispatch?` option for `use DoubleDown.Facade` — controls whether
  the generated facade includes the `NimbleOwnership`-based test handler
  resolution step. Accepts `true`, `false`, or a zero-arity function
  returning a boolean, evaluated at compile time. Defaults to
  `fn -> Mix.env() != :prod end`, so production builds get a config-only
  dispatch path with zero `NimbleOwnership` overhead (no
  `GenServer.whereis` ETS lookup).
- `DoubleDown.Dispatch.call_config/4` — config-only dispatch function that
  skips test handler resolution entirely. Used by facades compiled with
  `test_dispatch?: false`.

## [0.15.0]

### Added

- `pre_dispatch` option for `defcallback` — a generic mechanism for
  transforming arguments before dispatch. Accepts a function
  `(args, facade_module) -> args` declared at the contract level,
  spliced into the generated facade function as AST.
- `Repo.Test` tests split into dedicated `test/double_down/repo/test_test.exs`
  module.

### Changed

- 1-arity `transact` functions are now wrapped into 0-arity thunks
  at the facade boundary via `pre_dispatch`. The thunk closes over
  the facade module, so calls inside the function (e.g.
  `repo.insert(cs)`) go through the facade dispatch chain. This
  ensures facade-level concerns (logging, telemetry) apply in both
  test and production.
- `Repo.Test` and `Repo.InMemory` adapters no longer handle 1-arity
  transaction functions — they always receive 0-arity thunks (from
  `pre_dispatch` wrapping) or `Ecto.Multi` structs.
- The hardcoded `:transact` special-case in `DoubleDown.Facade` has been
  removed. The Repo-specific facade injection is now declared on the
  `defcallback` in `DoubleDown.Repo` using the generic
  `pre_dispatch` mechanism.

### Fixed

- User-supplied fallback functions in `Repo.InMemory` that raise
  non-`FunctionClauseError` exceptions (e.g. `RuntimeError`,
  `ArgumentError`) no longer crash the NimbleOwnership GenServer.
  Exceptions are captured and re-raised in the calling test process
  via `{:defer, fn -> reraise ... end}`.

## [0.14.0]

### Added

- `DoubleDown.Repo.insert_all/3` — standalone bulk insert
  operation, dispatched via fallback in both test adapters.
- `DoubleDown.Testing.set_mode_to_global/0` and `set_mode_to_private/0`
  — global handler mode for testing through supervision trees,
  Broadway pipelines, and other process trees where individual pids
  are not accessible. Uses NimbleOwnership shared mode. Incompatible
  with `async: true`.
- `DoubleDown.Repo.Autogenerate` — shared helper module for
  autogenerating primary keys and timestamps in test adapters.
  Handles `:id` (integer auto-increment), `:binary_id` (UUID),
  parameterized types (`Ecto.UUID`, `Uniq.UUID`, etc.), and
  `@primary_key false` schemas.
- `docs/migration.md` — incremental adoption guide covering the
  two-contract pattern, coexisting with direct Ecto.Repo calls, and
  the fail-fast test config pattern.
- Process-testing patterns in `docs/testing.md` — decision table,
  GenServer example, supervision tree example.

### Changed

- Test adapters (`Repo.Test`, `Repo.InMemory`) now check
  `changeset.valid?` before applying changes — invalid changesets
  return `{:error, changeset}`, matching real Ecto.Repo behaviour.
- Test adapters now populate `inserted_at`/`updated_at` timestamps
  via Ecto's `__schema__(:autogenerate)` metadata. Custom field
  names and timestamp types are handled automatically.
- 1-arity `transact` functions now receive the facade module instead
  of `nil`, enabling `fn repo -> repo.insert(cs) end` patterns.
- The internal opts key for threading the facade module through
  transact was renamed from `:repo_facade` to `DoubleDown.Repo.Facade`
  for proper namespacing.
- Primary key autogeneration is now metadata-driven — supports
  `:binary_id` (UUID), `Ecto.UUID`, and other parameterized types.
  Raises `ArgumentError` when autogeneration is not configured and
  no PK value is provided.
- Autogeneration logic extracted from `Repo.Test` and
  `Repo.InMemory` into shared `DoubleDown.Repo.Autogenerate` module.
- Repo contract now has 16 operations (was 15).

### Fixed

- Invalid changesets passed to `Repo.Test` or `Repo.InMemory`
  `insert`/`update` no longer silently succeed — they return
  `{:error, changeset}`.
- `Repo.InMemory` store is unchanged after a failed insert/update
  with an invalid changeset.

## [0.13.0]

### Added

- Fail-fast documentation for `impl: nil` test configuration.

### Changed

- Improved error messages when no implementation is configured in
  test mode.

## [0.12.0]

### Changed

- Removed unused Ecto wrapper macro.
- Version now read from `VERSION` file.

## [0.11.1]

### Changed

- Documentation improvements (README, hexdocs, testing guide).
- Removed unnecessary `reset` calls from test examples.

## [0.11.0]

### Fixed

- Fixed compiler warnings.

## [0.10.0]

### Added

- `Facade` without implicit `Contract` — `use DoubleDown.Facade` with
  an explicit `:contract` option for separate contract modules.
- Documentation explaining why `defcallback` is used instead of standard
  `@callback` declarations.

## [0.9.0]

### Added

- Single-module `Contract + Facade` — `use DoubleDown.Facade` without
  a `:contract` option implicitly sets up the contract in the same
  module.

### Changed

- Dispatch references the contract module, not the facade.

## [0.8.0]

### Added

- `DoubleDown.Repo` — built-in 15-operation Ecto Repo
  contract with `Repo.Test` (stateless) and `Repo.InMemory`
  (stateful) test doubles.
- `MultiStepper` for stepping through `Ecto.Multi` operations
  without a database.

### Changed

- Renamed `Port` to `Facade` throughout.
- Removed separate `.Behaviour` module — behaviours are generated
  directly on the contract module.

## [0.7.0]

### Changed

- `Repo.InMemory` fallback function now receives state as a third
  argument `(operation, args, state)`, enabling fallbacks that
  compose canned data with records inserted during the test.

## [0.6.0]

### Fixed

- Made `DoubleDown.Contract.__using__/1` idempotent — safe to `use`
  multiple times.

## [0.5.0]

### Changed

- Improved `Repo.Test` stateless handler.

## [0.4.0]

### Added

- `Repo.InMemory` — stateful in-memory Repo implementation with
  read-after-write consistency for PK-based lookups.
- NimbleOwnership-based process-scoped handler isolation for
  `async: true` tests.

## [0.3.1]

### Fixed

- Expand type aliases at macro time in `defcallback` to resolve
  Dialyzer `unknown_type` errors.

## [0.3.0]

### Added

- `transact` defcallback with `{:defer, fn}` support for stateful
  dispatch — avoids NimbleOwnership deadlocks.
- `Repo.transact!` for `Ecto.Multi` operations.

## [0.2.0]

### Changed

- Split `DoubleDown` into `DoubleDown.Contract` and `DoubleDown.Port`
  (later renamed to `Facade`).

## [0.1.0]

### Added

- Initial release — `defcallback` macro, `DoubleDown.Contract`,
  `DoubleDown.Testing` with NimbleOwnership, `Repo.Test` stateless
  adapter, CI setup, Credo, Dialyzer.

[Unreleased]: https://github.com/mccraigmccraig/double_down/compare/v0.28.1...HEAD
[0.28.1]: https://github.com/mccraigmccraig/double_down/compare/v0.28.0...v0.28.1
[0.28.0]: https://github.com/mccraigmccraig/double_down/compare/v0.27.0...v0.28.0
[0.27.0]: https://github.com/mccraigmccraig/double_down/compare/v0.26.0...v0.27.0
[0.26.0]: https://github.com/mccraigmccraig/double_down/compare/v0.25.0...v0.26.0
[0.25.0]: https://github.com/mccraigmccraig/double_down/compare/v0.24.0...v0.25.0
[0.24.0]: https://github.com/mccraigmccraig/double_down/compare/v0.23.0...v0.24.0
[0.23.0]: https://github.com/mccraigmccraig/double_down/compare/v0.22.0...v0.23.0
[0.22.0]: https://github.com/mccraigmccraig/double_down/compare/v0.21.0...v0.22.0
[0.21.0]: https://github.com/mccraigmccraig/double_down/compare/v0.20.0...v0.21.0
[0.20.0]: https://github.com/mccraigmccraig/double_down/compare/v0.19.0...v0.20.0
[0.19.0]: https://github.com/mccraigmccraig/double_down/compare/v0.18.0...v0.19.0
[0.18.0]: https://github.com/mccraigmccraig/double_down/compare/v0.17.0...v0.18.0
[0.17.0]: https://github.com/mccraigmccraig/double_down/compare/v0.16.1...v0.17.0
[0.16.1]: https://github.com/mccraigmccraig/double_down/compare/v0.16.0...v0.16.1
[0.16.0]: https://github.com/mccraigmccraig/double_down/compare/v0.15.0...v0.16.0
[0.15.0]: https://github.com/mccraigmccraig/double_down/compare/v0.14.0...v0.15.0
[0.14.0]: https://github.com/mccraigmccraig/double_down/compare/v0.13.0...v0.14.0
[0.13.0]: https://github.com/mccraigmccraig/double_down/compare/v0.12.0...v0.13.0
[0.12.0]: https://github.com/mccraigmccraig/double_down/compare/v0.11.1...v0.12.0
[0.11.1]: https://github.com/mccraigmccraig/double_down/compare/v0.11.0...v0.11.1
[0.11.0]: https://github.com/mccraigmccraig/double_down/compare/v0.10.0...v0.11.0
[0.10.0]: https://github.com/mccraigmccraig/double_down/compare/v0.9.0...v0.10.0
[0.9.0]: https://github.com/mccraigmccraig/double_down/compare/v0.8.0...v0.9.0
[0.8.0]: https://github.com/mccraigmccraig/double_down/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/mccraigmccraig/double_down/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/mccraigmccraig/double_down/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/mccraigmccraig/double_down/compare/v0.4.0...v0.5.0
[0.4.0]: https://github.com/mccraigmccraig/double_down/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/mccraigmccraig/double_down/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/mccraigmccraig/double_down/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/mccraigmccraig/double_down/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/mccraigmccraig/double_down/releases/tag/v0.1.0
