Version. 0.1
Date. 2026-06-19
Author. Marvin Percival (DUNIN7 Operator) with Claude.ai (scoping) on DUNIN7-M4.
For. A fresh Claude.ai chat — the CR-drafting role. This chat's product is the P7-2 change request: the second Stele Phase 7 packaging slice — the SDK surface and the mountable primitive router.
Status. CR-drafting handoff. P7-1 (CR-2026-114) is landed and recorded; the standalone stele repo is live and the engine consumes it as a package. The §E fork is resolved B-first (primitives-only). This chat drafts P7-2 only. Markdown primary (technical consumer / next-session orientation).
What this chat does. Drafts the P7-2 change request — the slice that gives stele an HTTP surface of its own. Today stele is a published library with no router (P7-1 confirmed: no APIRouter, no route decorators). The engine wires Stele's primitives into its own host routers. P7-2 builds, inside the stele package, a mountable primitive router plus the SDK surface a host app imports to mount it — so a downstream app can include_router(stele.router) and get the identity/credential/recovery/TOTP primitives without re-implementing the wiring the engine currently hand-rolls.
What P7-2 is under B-first (primitives-only). The §E fork (resolved in the scoping note) chose Option B: Stele ships primitive-level routes — the me_security / auth_dev shape (direct primitive calls: passkey enrollment, recovery-code regen, TOTP rotation, session issue). It does not ship the full /auth/signup/ + /auth/login/ orchestration — those stay host compositions over Stele primitives, because they call onboard (host account, credit, membership), and putting them in Stele would require a Stele→host callback seam B-first deliberately avoids. The marquee signup/login flows are demonstrated by the reference app (P7-3), not shipped in the package.
What P7-2 is NOT. Not the reference app, docs, or config generator (P7-3). Not the WebAuthn-ceremony-gap close (P7-3). Not the published-package path (the ../stele editable dep stays as P7-1 left it; retiring it is a parked thread). Not a schema change — Stele's 3 tables are settled (P7-1). Not a re-open of §E (resolved B).
The decision P7-2 must make explicitly (your raised question, now an input). The Stele session is a minted token. How that token is delivered differs between a web UI (cookie) and an agent (bearer/explicit-token). P7-2's router must not default to cookie-only. The session-delivery queued direction (in the input set) is the framing; P7-2 decides the router's delivery shape — cookie-only, both, or token-mint-only-with-host-owns-delivery. Under B-first the clean lean is likely Stele mints the token, the host owns delivery (accommodates both without Stele taking a position) — but that is a P7-2 finding to confirm against the router shape, not a foregone conclusion.
All in the input zip (stele-p7-2-cr-drafting-inputs-v0_1.zip, staged to ~/Downloads/):
loomworks-stele-phase-7-scoping-note-v0_1.md) — the commitment frame. §1 resolves the §E fork (B); §2 names the deliverable set under B; §3 sequences P7-1/2/3. P7-2 = §3's second slice. The B resolution is why P7-2 ships primitive routes, not orchestration.loomworks-stele-phase-7-step-0-inspection-report-v0_1.md) — the ground for the router. Load-bearing for P7-2: §B (the 18 host endpoints across three coupling depths — depth 1 = direct-primitive me_security/auth_dev, the P7-2 router target; depth 2 = service-mediated signup/login, host-stays; depth 3 = host-owned claim), §C (the persons.signup/persons.login service binding — the heaviest Stele surface, and what stays host-side under B), §D (the mint↔onboard cut line — mint_principal is Stele, onboard is host-pure, which is why B works without a callback seam), and §F.2/F.4 (the doubly-bound persons/signup.py and the session/cookie seam — both P7-2 concerns).loomworks-stele-phase-7-p7-1-completion-record-v0_1.md) — the as-built floor P7-2 stands on. What's already in stele (the 9 modules, KEK-direct TOTP, the 3-table baseline), the editable-dep precondition, the actor.py-stays-host decision, and the banked lessons. P7-2's router is built in the standalone repo and consumed by the engine — same two-repo shape P7-1 established.loomworks-queued-direction-p7-2-session-delivery-ui-vs-agent-v0_1.md) — the delivery decision input. Cookie (UI) vs bearer (agent) over the same Stele-minted token. P7-2 decides; this note frames.loomworks-candidate-seed-v0_12.md) — the authentication framework (no email as identity; passkey + authenticator code; recovery via credentials) and the authorization substrate (OVA as candidate access-control; the two-mode open/restricted access model). P7-2's router must be honest about what it assumes for authorization — see "The authorization-assumption question" below.current-status-manifest-v0_66.md) — the live state, P7-1 fold-in.DUNIN7/stele (private) main = 0d5e265 — standalone library + manifest + 3-table migration baseline; clone-and-run verified. No router yet.loomworks-engine main = ff0c4cf — consumes stele as an editable package (stele = ../stele); actor.py host-side; suite 2928/0/46.loomworks-record main = e1e7b8c — CR-2026-114 + P7-1 completion record + manifest v0.66.mint_principal (Stele) / onboard (host-pure). This is the architectural gift that lets B work without a Stele→host callback.Two deliverables. The CR drafts each from the Step 0 report's facts, opening with its own Step-0-of-the-CR grounding pass (CC inspects the live tree — both repos now) before committing to the build.
stele)
A FastAPI APIRouter inside the stele package, carrying the direct-primitive endpoints — the me_security / auth_dev shape from Step 0 §B depth 1. Candidate route set (confirm against the live engine routers at the CR's Step 0, since these are being lifted-and-generalized from host routers, not authored fresh):
auth_dev shape — confirm whether this is dev-only or generalizes)The CR must:
me_security handler takes person: Principal = Depends(get_current_principal) — and get_current_principal is a host dependency (api/deps.py), resolving the session cookie. A mountable router cannot assume the host's get_current_principal. The CR decides: does Stele's router ship its own current-principal dependency (over its own session lookup), or define a dependency interface the host supplies? This is the router's central contract question. (See "The authorization-assumption question.")LOOMWORKS_SECRET_KEY / the env-var the KEK provider reads), and the current-principal resolution. The mountable-router contract is this injection surface.stele)
The import surface a host app uses to mount the router and call the primitives — what stele exposes at its top level. The CR must:
stele's public API is: the router, the primitive service functions (the §C binding surface — mint_principal, the WebAuthn ceremony functions, credential/recovery/TOTP operations), the Principal type, the config/injection seam.include_router(...) + what dependencies it overrides/provides). Demonstrated minimally here; fully in the P7-3 reference app.stele.registry.mint_principal, etc.); the SDK surface must not break those import paths, or must re-point them in the same slice (the P7-1 add-and-verify discipline).
The seed names OVA as the candidate access-control substrate, not-yet-enforcing, with a two-mode (open/restricted) access model and a seam-marked stub. Stele's primitive router sits below that — it authenticates (proves credential control) and issues sessions; it does not authorize (decide what a principal may reach). But its get_current_principal dependency is the seam where authentication hands off to the host's authorization.
The CR must be honest about the boundary: Stele's router ships authentication primitives and session issuance; it assumes the host owns authorization (who-may-reach-what, the OVA seam). A mountable router that silently assumed the engine's specific authorization context would not be mountable elsewhere. Name what Stele provides (authenticated principal + session) vs what the host must provide (authorization over that principal). This keeps the router honest to the "stranger mounts it" bar.
../stele editable dep stays; retiring it is a parked thread, not P7-2.persons/signup.py splitting — Step 0 §F.2: it's doubly-bound (signup flow and me_security passkey enrollment). P7-2 lifts the me_security-shaped enrollment primitives into the router; whether the signup half of that module is touched depends on the lift's blast radius — a CR Step-0 question. The signup orchestration stays host either way (B-first).stele for the router/SDK build; engine for the re-point). The CR opens with its own Step-0 grounding pass.me_security/auth_dev handlers into a mountable shape. Ground the route set against the live host routers, but the result is new package code.stele, verify standalone, then re-point/extend the engine to consume it; engine suite green throughout; the engine never has a window where it can't import what it needs.loomworks-record at change-requests/.
P7-2 is likely CR-2026-115 (P7-1 was 114). Confirm against the live ledger at draft time — CC checks the highest cr-2026-NNN in loomworks-record/change-requests/; don't assume from this handoff. P7-3 takes the next when it drafts.
Two-role division holds: CR-drafting (Claude.ai) separate from execution (CC). Fresh chat per role.
5472335) — four boundary findings, A3 privilege-model HALT, awaiting CC. Orthogonal.DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Phase 7 P7-2 CR-Drafting Handoff — v0.1 — 2026-06-19