Version. 0.1
Date. 2026-06-19
Author. Marvin Percival (DUNIN7 Operator) with Claude.ai (scoping) on DUNIN7-M4.
Status. Scoping note. P7-2 (CR-2026-115) is landed across all three repos. Step 0 inspection (...-p7-3-step-0-inspection-report-v0_1.md, record 45ac2f1) is complete and both forks are resolved. This note fixes P7-3's commitment frame; the CR-drafting handoff (alongside) hands it to a fresh chat. Markdown primary (technical consumer).
Against. DUNIN7/stele 1077803, loomworks-engine 08e89c5, loomworks-record 45ac2f1 (re-confirm at the CR's own Step 0).
What P7-3 is. The final Phase 7 slice — the clone-and-run-and-use bar. After P7-3, a stranger can clone a reference host, run it, and use Stele's identity primitives end-to-end including registering a real passkey in a browser. P7-3 has four code deliverables plus docs.
The two forks are resolved (on the inspection's facts).
persons/signup.py split. add_passkey_ shares only two trivial one-directional symbols with the signup flow, is a thin wrapper over stele.webauthn primitives it already calls, carries no host concepts, and its types are already Stele-shaped. So P7-3 lifts add_passkey_ into stele.webauthn, dissolving the P7-2 asymmetry. The injection slot disappears; the engine re-points its import..env template (secret pre-generated, RP-ID / origin / RP-name to fill), no code-generation, can't-drift.Trajectory preserved (correction named). Scoping entered Fork 1 assuming the lift reopened a parked split and leaning toward "wire." The inspection inverted that — the split doesn't exist; the lift is cheap. The decision flipped on facts, and Fork 2 collapsed out of Fork 1. The P7-2 completion record's §4 named this asymmetry as a "correction-in-waiting" to dissolve when cheap; it is cheap; P7-3 is when.
Sequenced so the lift lands first (everything downstream shrinks once it does).
Extract add_passkey_begin / add_passkey_complete (+ private helpers + the PendingAddPasskeyStore / AddPasskeyBeginResult types) from loomworks-engine/src/loomworks/persons/signup.py into DUNIN7/stele/src/stele/webauthn (or the natural stele home the CR's Step 0 confirms). Handle the two shared symbols (PENDING_TTL, PendingRegistrationNotFound) cleanly — the inspection found them trivial and one-directional; the CR's Step 0 confirms the exact handling (likely they move with the ceremony or become Stele-owned).
stele.router's provide_add_passkey_begin / _complete slots are removed; the router calls the now-internal stele.webauthn ceremony directly. The provide_add_passkey_store slot's fate is a CR Step-0 question (does the store become Stele-internal too, or stay a host-supplied slot? — the inspection said the types are Stele-shaped, which leans toward internal).me_security (and anything else) imports the ceremony from stele.webauthn instead of persons.signup. Two-repo add-and-verify (P7-1/P7-2 discipline): build/verify in stele, re-point the engine, engine suite green throughout.persons/signup.py shrinks to the signup-flow functions only; the co-located ceremony block is deleted post-lift. Confirm the signup flow is fully intact (the inspection said removing the ceremony leaves it untouched).
A minimal standalone FastAPI app — not the engine — that mounts stele.router and demonstrates real end-to-end use.
stele.router is a plain APIRouter mountable in any FastAPI app).0001_baseline) to stand up its DB.extract_token (the router defaults to bearer; the app shows the cookie override).
A minimal HTML/JS front that runs the real navigator.credentials.create() / .get() WebAuthn ceremony against the mounted router — so a user opens the reference app in a browser, registers a real passkey, and it round-trips through stele.router end-to-end. This is the "clone and run and use it with a real authenticator" proof the engine's synthetic-payload mount-verify test could not show. The RP-ID / origin binding (from the webauthn config) is what the browser ceremony needs.
A small helper that emits a .env template: STELE_SECRET_KEY (freshly generated), STELE_RP_ID, STELE_ORIGIN, STELE_RP_NAME (the fields the webauthn config needs — confirm the full set at the CR's Step 0 against the live config shape). No code generation. The adopter copies the file, fills the site-specific fields, runs. Can't-drift because it emits no code.
What each remaining injection slot is, what the host supplies, the authorization boundary (Stele authenticates, host authorizes), the cookie-vs-bearer delivery choice, and the minimal-mount walkthrough the reference app embodies. Plain-English standard. Format: the docs are operator/adopter-facing — HTML primary with Markdown source, per convention; the reference-app code + README is Markdown/technical.
Decision 1 — the provide_add_passkey_store slot's fate (rides with the lift). Post-lift, does the pending-passkey store become Stele-internal (the inspection found its types Stele-shaped, leaning this way) or stay a host-supplied slot? If internal, the mandatory slot set shrinks further (and the config story simplifies again). The CR's Step 0 confirms whether the store has any host-specific binding that would keep it a slot. Scoping lean: internal if it's clean (consistent with dissolving the asymmetry fully), but confirm at Step 0.
Decision 2 — the login-TOTP composition gap. Stele has a TOTP rotation primitive but no standalone TOTP-verify primitive (inspection §D). A reference app demonstrating full login (passkey + TOTP second factor) must verify a TOTP code. Two paths:
Scoping lean: host-composition — it's B-first-consistent and avoids growing Stele's primitive surface inside the reference-app slice. But there's a real case for the primitive (Stele owns TOTP rotation; not owning verify is a smaller cousin of the add_passkey asymmetry we're dissolving). The CR-drafting chat weighs this against the live primitive surface and decides; if it leans toward the primitive, that's a fifth small Stele deliverable to name explicitly.
../stele editable dep stays; retiring it for a version pin is a parked thread. (Note: the lift adds engine→Stele coupling via a new import path, but the dep mechanism is unchanged — still the editable path dep.)stele.router — the engine keeps its hand-rolled routers; P7-3 does not mount stele.router in the engine's production surface.stele examples/ dir, a separate repo, or a record-adjacent home — Operator's filing call surfaced at Step 0).stele, re-point the engine, suite green throughout; the engine never loses an import it needs.DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Phase 7 P7-3 Scoping Note — v0.1 — 2026-06-19