DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/stele-extraction-phase-7/loomworks-stele-phase-7-p7-3-scoping-note-v0_1.md

Loomworks — Stele Phase 7 P7-3 Scoping Note — v0.1

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).


Plain-language summary

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).

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.


What P7-3 commits to — four code deliverables + docs

Sequenced so the lift lands first (everything downstream shrinks once it does).

1. The ceremony lift (do first; both repos)

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).

2. The reference app (standalone host)

A minimal standalone FastAPI app — not the engine — that mounts stele.router and demonstrates real end-to-end use.

3. The WebAuthn-ceremony-gap close (the browser front)

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.

4. The config generator (env-scaffolding-only)

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.

5. The docs (the mount contract written down)

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.


The two §D decisions the CR must make (framed; the CR-drafting chat resolves with its own Step 0)

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.


Held out of P7-3 scope (named so they're not pulled in)


Sequencing

  1. Lift first (deliverable 1) — both repos, add-and-verify, engine suite green. This shrinks the slot set the rest builds against.
  2. Reference app + browser front + config gen (deliverables 2–4) — built in a new reference-app location (the CR's Step 0 decides where: a stele examples/ dir, a separate repo, or a record-adjacent home — Operator's filing call surfaced at Step 0).
  3. Docs (deliverable 5) — written against the as-built reference app.
  4. Record — completion record + manifest bump; CR-2026-115 asymmetry-dissolution recorded.

Standing disciplines to carry into the CR


DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Phase 7 P7-3 Scoping Note — v0.1 — 2026-06-19