@@ 264,3 264,38 @@ Rollback per phase: each commit is independently revertable. PH3 alone reverts c
- Verify-driven fix-up — `OIDCVerifier.Cfg` retyped from `config.OIDCConfig` to `config.AuthConfig`; `Init` rewired to read `Cfg.OIDC.<field>`. **Why:** smoke test surfaced a pre-existing latent steward-injection bug (origin `80b1c09`) that prevented lethe from booting with `oidc.enabled=true`. Fix is sibling-consistent with `Authenticator` (middleware.go:84) and `OIDCDevStub` (devstub.go:28). Two test call sites updated to wrap in `config.AuthConfig{OIDC: ...}`.
- Verify-driven fix-up — `OIDCDevStub` registered with `steward.Root()` modifier in `cmd/lethe/main.go:137`. **Why:** smoke test surfaced steward ERR/WRN `empty dependents asset without root option asset=auth.OIDCDevStub` because the dev stub has no in-process dependents (it's a side-effect listener). Adding `steward.Root()` silences the warning AND makes shutdown call `OIDCDevStub.Destroy` (verified in shutdown log).
+## Verify
+
+**Result:** passed
+
+Positive:
+- CK1 — `go test -count=1 ./internal/testutil/oidcstub/...` → 5/5 pass
+- CK2 — `go test -count=1 ./internal/server/auth/...` → 24/24 pass (covers IV5 — refactored middleware_test cases unchanged in semantics)
+- CK3 — `go test -count=1 ./internal/config/...` → 18/18 pass (incl. new positive + non-loopback-rejects fixtures)
+- CK4 — `go build ./cmd/lethe` → clean
+- CK5 — Smoke: lethe boots, `GET /api/v1/sessions` with bearer from `/dev/token` → 200
+
+Negative:
+- CK6 — `GET /api/v1/sessions` no bearer → 401 UNAUTHORIZED problem+json
+- CK7 — `GET /api/v1/sessions` `Bearer not-a-jwt` → 401
+- CK8 — `GET /dev/token` no `?sub=` → 400 `{"error":"sub is required"}`
+- CK9 — config with `dev_stub.bind: "0.0.0.0:8191"` rejected at load: `'Bind' failed on the 'loopback_bind' tag` (CONFIG_VALIDATE)
+
+Invariants / assumptions:
+- CK10 (IV1) — `oidcstub` imports stdlib + `go.bigb.es/auxilia/culpa` only
+- CK11 (IV2) — `TestOIDCDevStub_InitStartsListener_TokenVerifies` exercises listener-up-before-discovery; smoke confirms in real process
+- CK12 (IV3) — `TestOIDCDevStub_DisabledIsNoop` covers default-disabled no-listener path
+- CK13 (IV4) — smoke: stub-minted token accepted by `OIDCVerifier` driven from the same `AuthConfig` — iss/aud/username_claim agree by construction
+- CK14 (IV5) — covered by CK2
+- CK15 (AS1) — unverifiable at this layer (operator-only choice)
+- CK16 (AS2) — `coreos/go-oidc/v3` accepted `http://127.0.0.1:8191` in smoke
+- CK17 (AS3) — `rsa.GenerateKey` at `oidcstub.go:66` inside `New` — fresh per construction
+
+Interfaces:
+- CK18 (IF1) — 7 `oidcstub.New(oidcstub.Options{...})` call sites, all match `(Options) (*Stub, error)`
+- CK19 (IF2) — `stub.Mint(sub string, ttl time.Duration, extra map[string]any)` called at `devstub.go:124` and `oidcstub_test.go:94` matching declared 3-tuple return
+- CK20 (IF3) — `stub.Handler()` passed to `http.Server.Handler` (`devstub.go:70`) and `httptest.NewServer` (test sites)
+- CK21 (IF4) — `config.OIDCDevStubConfig{Enabled, Bind, TokenTTL}` constructed at `devstub_test.go:58/144`; struct definition at `config.go:80`
+
+Smoke: `./tmp/lethe-verify -config tmp/dev-stub-verify.yaml` → discovery 200, mint OK, sessions 200 with bearer / 401 without; clean shutdown including `destroying component=auth.OIDCDevStub`.
+