SIGN IN SIGN UP

feat(state): the three credential directories, and the two answers that must not merge

Dimension 5.5. A new crate, `afd_state`, named for `src/agentsfleetd/state/`
where the Zig daemon keeps its statements. Integration goes 31 → 43 tests, all
against live Postgres.

## Where these had to live

`src/auth/` may not import `src/db/` and `make test-auth` greps to keep it that
way; `afd_auth` reaches the same wall by construction, since it does not list
`sqlx` and so cannot name it. Zig's concrete lookups therefore sit in
`cmd/api_key_lookup.zig`, `cmd/cli_credential_lookup.zig` and
`cmd/serve_runner_lookup.zig` — three files, three `LookupFn` pointers, three
`Ctx` structs each holding the same pool.

Here it is one type holding one pool and one `resolve` that dispatches on the
class the caller already has in hand. The plumbing was three times as wide to
say the same thing.

## The distinction the trait exists for

`Ok(None)` and `Err(Unavailable)` must never collapse. A digest matching nothing
is an authentication REJECTION; a Postgres blip is not. The runner client counts
rejections toward a self-termination ceiling and resets that counter on
transport-class failures, so reporting an outage as a rejection walks a healthy
fleet's runners to shutdown one at a time.

Two tests hold that line from opposite sides — a statement Postgres refuses
(`DROP TABLE` under a live pool) and a datastore that is gone entirely
(`DROP DATABASE ... WITH (FORCE)` under one) — and both must answer unavailable.

## A corrupt row is unavailable, not unknown

A third case the trait has no obvious answer for. `Ok(None)` would tell a runner
its token is unknown, which it counts; `Unavailable` is also simply true, and it
pages the right person. Both paths log at `error`, because that is the only
signal the row exists at all.

Reachable in a test, which took reading the schema: `ck_runners_id_uuidv7`
checks the version nibble and nothing else, so a UUID with an RFC-4122-invalid
variant is a value Postgres stores and `Uuid7::parse` refuses. `oidc_subject`
has no non-empty constraint, so a blank subject is seedable too.

## `#[non_exhaustive]` made `CredentialKind`'s own doc comment false

The comment promises "the build fails until all three exist". The attribute
three lines below it forces every crate OUTSIDE `afd_auth` to write a `_` arm —
and the crate that most needs exhaustiveness is outside by construction. A new
credential class would have fallen into a catch-all and resolved to nothing,
silently. It buys downstream semver stability for crates that have no
downstream. Removed; four others remain in `afd_auth` and want the same look.

## Rows read through `FromRow`, not column at a time

Reading columns one by one gives every read its own error arm, and those arms
are unreachable in a correct build — dead code this workspace measures. A
hand-written `FromRow` per class folds all of it into the one failure sqlx
already reports, so the lookup has a single error path and it is one a test can
reach by cutting the connection. Written out rather than derived, for the same
reason the problem+json envelope builds a `Map`.

## Also fixed

`test_entries_match_the_zig_registry` had no Zig spelling recorded for 429, so
the first entry to answer that status broke it. The registry test now knows
`.too_many_requests` and `.not_found`.

`Entropy`'s `Default` was the workspace's only uncovered line. Covered by an
assertion worth having on its own: the default source is the operating system's,
not the mock behind `test-util` — a `Default` reaching for that would make every
minted id predictable in any build with the feature on.
K
Kishore Kumar committed
66bea03a4ba9c4774c3174e2afd52ab8a3db681d
Parent: 6f70680