# `Linx.Reconcile.Source`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/reconcile/source.ex#L1)

The plug-in contract that lets the generic `Linx.Reconcile` loop drive any
reconcilable subsystem — deliberately minimal.

Each reconcilable subsystem keeps its own rich, type-bearing surface
(`Linx.Sysctl.Reconcile`, `Linx.Netlink.Rtnl.Reconcile`, …) for consumers
that want direct control. This behaviour is the *narrow* seam the loop drives,
implemented by a small adapter that delegates to that surface — so the loop
stays subsystem-agnostic and the per-subsystem APIs stay free of a
lowest-common-denominator façade.

Two layers, then:

  * the per-subsystem `reconcile/3,4` a consumer calls directly when it wants
    control, and
  * this uniform contract the `Linx.Reconcile` loop drives.

## The `scope`

A `scope` names the namespace the source acts in, in whatever shape that
subsystem already accepts. It is an opaque value to the loop, threaded
verbatim into every callback:

  * sysctl — the `:in` target (`:self`, `{:pid, n}`, `{:path, p}`);
  * rtnl — a `t:Linx.Netlink.Socket.netns/0` (`:host`, `{:pid, n}`,
    `{:path, p}`), the source opening a short-lived socket per pass.

## The report contract

`reconcile/4` returns `{:ok, report}` on a completed pass (even a partial one
— per-op failures live *inside* the report). The loop relies on two fields
being present on that report, by convention shared across every subsystem's
report struct:

  * `:converged?` — `boolean()`, whether the pass left nothing undone;
  * `:last_applied` — the updated ownership state to thread into the next
    pass.

Both `Linx.Sysctl.Reconcile.Report` and `Linx.Netlink.Rtnl.Reconcile.Report`
carry these (alongside `:applied`/`:failed`/`:pending`). `{:error, reason}` is
reserved for a pass that could not run at all (could not observe the kernel,
an invalid desired state) — the loop keeps the previous `last_applied` and
retries on the next tick.

## Litmus test

This contract is also the go/no-go test for whether the opt-in loop is worth
shipping: if subsystems implement these callbacks by clean delegation, the
loop is trivially worth it; if atomicity or ownership force contortions, we
stop at the single-shot `reconcile` + Monitor primitives and let consumers
wrap them directly. An out-of-tree PoC and the sysctl/rtnl adapters cleared it.

# `desired`

```elixir
@type desired() :: term()
```

The desired state, in the subsystem's own desired-state shape.

# `last_applied`

```elixir
@type last_applied() :: term()
```

Reconciler-held ownership, threaded between passes. `%{}` is empty.

# `observed`

```elixir
@type observed() :: term()
```

Observed kernel state, in the subsystem's own observed shape.

# `opts`

```elixir
@type opts() :: keyword()
```

Subsystem-specific options, forwarded verbatim to the per-pass verb.

# `report`

```elixir
@type report() :: %{
  :converged? =&gt; boolean(),
  :last_applied =&gt; last_applied(),
  optional(atom()) =&gt; term()
}
```

A reconcile report. Must expose at least `:converged?` (boolean) and
`:last_applied` (the next ownership state) — see the moduledoc.

# `scope`

```elixir
@type scope() :: term()
```

Opaque namespace handle, in the shape the subsystem already accepts.

# `observe`
*optional* 

```elixir
@callback observe(scope(), opts()) :: {:ok, observed()} | {:error, term()}
```

Observes current kernel state in `scope`. Optional — the loop does not call
it (it drives `reconcile/4`, which observes internally); it rounds out the
contract for consumers and diagnostics.

# `reconcile`

```elixir
@callback reconcile(scope(), desired(), last_applied(), opts()) ::
  {:ok, report()} | {:error, term()}
```

Runs one reconcile pass in `scope` against `desired`, given the prior
`last_applied`. Returns `{:ok, report}` on a completed (possibly partial)
pass, or `{:error, reason}` if the pass could not run at all.

# `subscribe`

```elixir
@callback subscribe(scope(), owner :: pid()) ::
  {:ok, pid()} | {:error, term()} | :unsupported
```

Starts a Monitor delivering change hints to `owner`, for the loop's low-latency
wakeups. Returns `{:ok, monitor_pid}`, `:unsupported` (the loop runs
timer-only), or `{:error, reason}`.

The loop treats every message it then receives as a level-triggered "look now"
hint — it never acts on an event payload — so the monitor's exact message
shape is its own concern.

---

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