# `Ankusa.Intake.Reconciler`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/intake/reconciler.ex#L1)

Carries half-persisted hooks to completion (ADR 0002, sections 3 and 5). Under
`wal: :dual` a record the writer issued to both places may end with only one of
them: the local store (the log refused or timed out) or the log (the store
write failed). The provider got a `503`, and the half that persisted still
exists, so the record is **completed, never undone**:

  * log half missing: publish it again (up to 256 records at a time)
  * local half missing: `Queue.Writer` re-applies the batch's ops, after one
    point read says the first write did not land after all
  * both missing: local first (the cheaper half, and it makes the hook
    deliverable), then the log

Retries back off per record from 100 ms to 5 s, jittered; a `throttled` class
honours its `retry_after`; a `permanent` class (the record can never fit, the
topic is gone) never completes and is *stuck* straight away. Nothing is ever
dropped. A record not completed within `intake.reconcile_deadline_ms` is
stuck: its fleet dedupe claim is released (a retry is then admitted as a new
hook with the same `idempotency_key`, never an acked loss) and the reconciler
keeps trying.

The claim of a carried hook is neither promoted nor released until both halves
are confirmed (`hold/3`); the reconciler promotes it then.

This is the live component. Boot-time recovery is `Ankusa.Intake.Recovery`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `finish`

```elixir
@spec finish(atom(), [tuple()], :local | :log) :: :ok
```

Settles the claims of entries a pass or the writer completed.

# `hold`

```elixir
@spec hold(atom(), String.t(), term()) :: :held | :completed | :stuck
```

Hand the reconciler the dedupe claim of carried hook `id`. `:held`: it will
promote it when both halves are confirmed. `:completed`: it already finished,
promote it yourself. `:stuck`: release it yourself. ETS only; no call.

# `start_link`

# `wake`

```elixir
@spec wake(atom()) :: :ok
```

Run a pass now (the writer calls it after it carried a batch).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
