DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/stele-extraction-phase-7/loomworks-stele-phase-7-p7-3-step-0-inspection-brief-v0_1.md

Loomworks — Stele Phase 7 P7-3 Step 0 Inspection Brief — v0.1

Version. 0.1 Date. 2026-06-19 Author. Marvin Percival (DUNIN7 Operator) with Claude.ai (scoping) on DUNIN7-M4. For. Claude Code (CC) on DUNIN7-M4 — a read-only inspection pass that grounds the P7-3 scoping note. CC inspects; CC decides nothing; CC reports facts and surfaces the two forks with the real picture so the Operator decides. Status. Step 0 inspection brief. P7-2 (CR-2026-115) is landed and recorded across all three repos. This brief precedes the P7-3 scoping note + CR. Markdown primary (technical consumer). Against. DUNIN7/stele main = 1077803, loomworks-engine main = 08e89c5, loomworks-record main = cb367ab (re-confirm at the top of the pass; record actual HEADs).


Plain-language summary

What P7-3 is. The final Phase 7 slice — the clone-and-run-and-use bar. A from-scratch standalone host app (NOT the engine) that mounts stele.router, wires the injection slots, and demonstrates real end-to-end use including the browser WebAuthn ceremony (a user registers a real passkey against the mounted package). Plus docs (the mount contract written down), a config generator (sized by this inspection), and the WebAuthn-ceremony-gap close. Both delivery shapes (cookie + bearer) demonstrated.

What this brief does. Grounds P7-3's scoping in the real post-P7-2 code, before any CR drafts. It has three jobs: (1) capture the live mount surface a stranger actually wires; (2) inspect the persons/signup.py tangle so the Operator can resolve the §4 ceremony fork on facts, not a guess; (3) report what a real mount actually requires so the config generator gets sized to fit. CC reports; the Operator decides both forks.

What this brief is NOT. Not a build. Not the scoping note (that follows, written against this brief's findings). No edits, no commits, no migrations — read-only throughout.


The two forks this inspection must inform (framed; CC reports, Operator decides)

Fork 1 — the §4 ceremony. P7-2 left the add-passkey enrollment ceremony (persons.signup.add_passkey_begin / add_passkey_complete) host-side, wired into stele.router as an injection slot. Stele owns all other WebAuthn logic (stele.webauthn); this one enrollment ceremony is the exception, parked because persons/signup.py is doubly-bound (signup flow + me_security enrollment). P7-3's reference app must do something about this slot. Two paths:

What CC must report for Fork 1: how tangled persons/signup.py actually is — whether lifting add_passkey_* is a clean extraction or a messy split. This fact is the whole decision. See §A.

Fork 2 — the config generator size. P7-3 includes a config generator, but its size is deliberately unset. It could be env-scaffolding-only (a .env template with STELE_SECRET_KEY / RP-ID / origin, secret pre-generated) or env-plus-a-runnable-mount-skeleton (a starter main.py with include_router(stele.router) and stubbed slots). The first is small and can't drift; the second is more useful but must track the mount shape.

What CC must report for Fork 2: what a real mount actually requires — the full set of config values and the full set of code-wiring a stranger writes to get a working mount. The size of that real surface decides whether a skeleton-file generator earns its keep or whether env-scaffolding is enough. See §C.


§A — The persons/signup.py tangle (Fork 1's deciding fact)

Inspect loomworks-engine/src/loomworks/persons/signup.py and report:

  1. The module's full surface — every top-level function/class, grouped by what it serves: the signup flow (begin_signup, complete_passkey, complete_totp_verify, etc.), the me_security enrollment ceremony (add_passkey_begin, add_passkey_complete, the PendingAddPasskeyStore and AddPasskeyBeginResult types), and anything serving both.
  2. The dependency lines between the two halves. Does add_passkey_ share helpers, types, constants, or module-level state with the signup-flow functions? List every shared symbol. The question lifting turns on: can add_passkey_ (+ its private helpers + its types) be extracted into stele.webauthn without dragging signup-flow code, or are they entangled?
  3. **What add_passkey_* imports from stele.webauthn already.** If the ceremony is mostly a thin wrapper over existing stele.webauthn registration primitives, the lift is near-trivial (it's already calling the Stele functions; lifting moves the thin wrapper). If it holds substantial host-specific logic, the lift carries that logic across. Report which.
  4. **Who else calls add_passkey_*.** Confirm the only callers are me_security's enrollment routes (Step 0 of P7-2 found this; re-confirm against live). If a signup-flow path also calls them, the lift is more entangled.
  5. The stele.webauthn landing site. If add_passkey_* were lifted, where in stele.webauthn would it land — does the module's existing shape have a natural home for an add-passkey ceremony, and would lifting require new Stele types (the PendingAddPasskeyStore / AddPasskeyBeginResult shapes — are these Stele-shaped or host-shaped)?

Report, do not decide. Output a clean read of the tangle. The Operator resolves Fork 1 from it.

§B — The live mount surface (what a stranger actually wires)

Inspect DUNIN7/stele/src/stele/api.py and src/stele/__init__.py (the P7-2 router + SDK) and report the mount surface as it exists, so the reference app builds against the real thing, not the CR's description:

  1. The eight routes — confirm the route set, methods, paths (resource-relative), and for each, which injection slots it pulls.
  2. The injection slots — the full list, each slot's name, its default (working default / Stele-env default / raises-until-overridden), and its exact callable signature. (P7-2's completion record §3 has the as-built table; re-confirm against live api.py.)
  3. The public SDK surface — what stele exposes at top level (the 65 primitives, the lazy stele.router, the slots), and the exact import paths a host uses.
  4. The mount mechanics — confirm the mount shape (module-level stele.router + app.dependency_overrides keyed on the exact slot objects). Report exactly what a host writes to override a slot (the app.dependency_overrides[slot] = provider form, with the real slot object names).
  5. The delivery seamextract_token (bearer default), how a host overrides it for cookie, and issue_session (mint-only). Report what a host writes for each of the two delivery shapes (cookie + bearer) so the reference app can demonstrate both.
  6. The WebAuthn config requirement — what provide_webauthn_config must return (stele.webauthn.WebauthnConfig), and specifically what RP-ID / origin / RP-name fields it carries. This is the config the generator must scaffold (§C) and the ceremony needs (Fork 1).

§C — What a real mount requires (Fork 2's sizing fact)

Synthesize §B into the concrete answer to "what does a stranger write to get a working mount":

  1. The config values — every env var / config field a host must set (secret key, RP-ID, origin, RP-name, anything else). This is the env-scaffolding floor.
  2. The code-wiring — every slot a host must supply a provider for (the raises-until-overridden set: db session, webauthn config, add-passkey store, the ceremony), and the minimal real provider each needs. Count them; estimate how many lines of host code a minimal working mount actually is.
  3. The WebAuthn ceremony requirement — what the browser side needs (the navigator.credentials.create() / .get() calls, the RP-ID/origin binding) to drive a real passkey enrollment against the mounted router. This is the ceremony-gap-close surface; report what the reference app's browser front must do.
  4. The sizing read — given (1)+(2), report whether the real mount is small enough that env-scaffolding-only suffices, or substantial enough that a runnable skeleton file would meaningfully cut the stranger's work. Report the fact; the Operator sizes the generator.

§D — Reference-app shape signals (reported, not designed)

So the scoping note builds the reference app against real constraints:

  1. What framework the reference app should be — confirm stele.router is a plain FastAPI APIRouter mountable in any FastAPI app, so the reference app is a minimal standalone FastAPI app (not the engine, not Next.js). Report any coupling that would complicate a bare-FastAPI mount.
  2. The DB requirement — the reference app needs a real DB for the routes to resolve (the credential/recovery/TOTP tables). Report whether Stele's migration baseline (the 3-table 0001_baseline from P7-1) is what the reference app runs to stand up its DB, and whether a throwaway SQLite or a real Postgres is needed (the routes use AsyncSession; report what the primitives assume).
  3. The browser-front minimum — what the smallest HTML/JS front needs to drive enrollment + login end-to-end (the navigator.credentials ceremony, the fetch calls to the mounted routes, the token handling for cookie vs bearer). Report the shape; the scoping note designs it.
  4. Login as well as enrollment — the router ships credential-backed routes (enrollment, recovery, TOTP rotation) but signup/login orchestration stayed host-side (B-first). Report what a reference app must compose over the Stele primitives to demonstrate a full login (not just enrollment) — this is host-composition the reference app writes, and the scoping note needs to know its shape.

Held out of P7-3 scope (named so the inspection doesn't chase them)

Output

A Step 0 inspection report (loomworks-stele-phase-7-p7-3-step-0-inspection-report-v0_1.md, Markdown primary) carrying:

Then halt — the Operator resolves both forks, and the P7-3 scoping note is written against this report.


Standing disciplines


DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Phase 7 P7-3 Step 0 Inspection Brief — v0.1 — 2026-06-19