Version. 0.1
Date. 2026-06-19
Author. Marvin Percival (DUNIN7 Operator) with Claude.ai (CR drafting) on DUNIN7-M4; CC executing on DUNIN7-M4.
Status. Completion record. CR-2026-114 (Stele Phase 7 P7-1 — Packaging Floor) is built, verified, and pushed: the standalone DUNIN7/stele repo (private) is live, and loomworks-engine consumes it as an installed package (merged-equivalent — pushed to main). Markdown primary (technical consumer).
Governing CR. change-requests/cr-2026-114-stele-phase-7-p7-1-packaging-floor-v0_1.md (record a5814ce).
Grounding line at execution. engine main = fe7c3f1 (CR-B merge) at Step-0; suite 2928/0/46. Re-confirmed fe7c3f1 before the engine re-point.
What P7-1 did. Lifted Stele out of the engine monorepo into its own standalone repository (DUNIN7/stele, private), gave it its own dependency manifest, and gave it an independently-runnable migration set — then re-pointed the engine to consume stele as an installed package. The done-bar held: a fresh clone installs from its own manifest and builds the three Stele tables against a fresh database with zero DUNIN7/Loomworks dependency.
What it did NOT do (held to scope): no SDK surface, no mountable router (P7-2); no reference app or docs (P7-3); did not re-open the §E router fork (resolved B / primitives-only); did not split persons/signup.py or decide the cookie helpers (P7-2).
The three decisions the CR made, as-built:
actor.py stayed host-side — Stele's standalone library is 9 modules (the 10th, actor.py, constructs the host's ActorRef; it remains in the engine, re-pointed to import stele.registry).down_revision = None), not a revision lift.data_encryption_keys.The one behavior change (TOTP secret: envelope → KEK-direct) resolved to a verified no-op against live data — see §3a.
DUNIN7/stele (private; main = 0d5e265) — a standalone repo:
src/stele/ — the 9 core modules (__init__, base, models, credentials, recovery, registry, session, person_totp, webauthn) plus kek (the slim KEK-direct crypto floor, lifted+slimmed from the engine's loomworks.credentials.{kek,envelope}). Zero loomworks imports.pyproject.toml — deps ==-pinned to the engine's resolved versions (sqlalchemy 2.0.49, pydantic 2.13.0, webauthn 2.7.1, pyotp 2.9.0, bcrypt 5.0.0, cryptography 46.0.7, asyncpg 0.31.0, alembic 1.18.4).migrations/ — own alembic root + one consolidated baseline (0001_baseline) building principals / webauthn_credentials / recovery_codes.
loomworks-engine (main = ff0c4cf, pushed) — re-pointed to consume the package: loomworks.stele. → stele. across 185 .py; stele added as an editable path dep; the 8 local module copies removed (actor.py kept).
stele repo (4 commits, DUNIN7/stele main 0d5e265):
| # | Commit | What |
|---|---|---|
| 1 | b628a96 | Initial lift: 9 core modules verbatim from engine fe7c3f1 (actor.py excluded) |
| 2 | 2ac288f | Standalone adaptation: KEK-direct TOTP crypto (stele/kek.py) + imports re-pointed loomworks.→stele. |
| 3 | d9a386d | D2: dependency manifest (pinned) + README |
| 4 | 0d5e265 | D3: alembic root + consolidated 3-table baseline |
The verbatim lift was committed first so the standalone adaptation reads as an auditable diff against the engine snapshot.
engine (2 commits, loomworks-engine main ff0c4cf, pushed):
| # | Commit | What |
|---|---|---|
| 1 | 5d92cf5 | Re-point: consume stele (uv add --editable ../stele; 185 .py re-pointed; actor.py preserved → stele.registry) |
| 2 | ff0c4cf | Remove the 8 local loomworks.stele copies (by-the-9; actor.py + __init__.py kept) |
Suite 2928 / 0 / 46 at every engine commit (after re-point; after removing copies). Add-and-verify throughout: the engine never had a window where it could not import what it needed (install → re-point → verify → remove).
3a — §6 re-wrap resolved to a verified NO-OP; the CR's "prevent lockout" premise was false against live data. The CR carried the TOTP envelope→KEK-direct switch as a migration that must re-wrap existing envelope-encrypted secrets "or lock principals out." Two findings, both recorded:
decrypt_value already dual-paths KEK-direct — its non-enveloped/legacy branch decrypts a bare Fernet token. So no lockout window ever existed; the engine reads KEK-direct tokens unchanged.playground_dev, 38 principals carry a totp_secret; 0 are envelope-encrypted (lwenv1:); all 38 are already KEK-direct (bare). Blast radius = 0. Confirmed real: 5/5 sampled bare tokens decrypt KEK-direct under the engine's KEK to valid 32-char base32 secrets.totp_secret writes stay bare (as the existing corpus already is), and the engine's decrypt_value continues to read them.
3b — the why-thread: envelope SCOPE_TOTP secrets were expected but absent (a walkable thread about CR-2026-102's landed state, not a P7-1 defect).
The engine's registry.mint_principal / person_totp called encrypt_for_scope(SCOPE_TOTP, …) (the envelope path, which prefixes lwenv1:), yet zero lwenv1: TOTP secrets exist on playground_dev. The likely explanations: CR-2026-102's Step-3/4 SCOPE_TOTP call-site repoint / data migration never actually landed for these secrets (the key-custody migration script was recorded as gated/not-run), or the 38 values are mirrored-in legacy secrets from the persons→principals expand window (written bare before envelope) and no fresh envelope-writing signup has occurred since. Flagged as a thread to walk about CR-2026-102's true landed state — it does not affect P7-1 (the standalone is KEK-direct; the engine reads bare tokens), but it means the engine's intended TOTP envelope encryption was not actually in effect for this corpus.
3c — the engine consumes stele via a relative-path editable dep (CR-sanctioned local install).
engine/pyproject.toml now carries stele = { path = "../stele", editable = true } + the lock entry. This is the CR's sanctioned "editable/local path install at this stage" (§102; the published-package path is a later slice). Deploy-time implication (recorded): the engine requires the stele repo present at ../stele (i.e. both repos under the same parent) until the published-package slice replaces the path dep with a version pin. Today both sit under /Users/dunin7/.
3d — two live-DB schema facts the ORM model alone would have missed, reproduced in the baseline.
The CR's "read the live DB, not the model shorthand" grounding caught: (a) webauthn_credentials.id and recovery_codes.id carry gen_random_uuid() server defaults while principals.id does not (the 0029 vs 0085 origin), and (b) two migration-only indexes — idx_webauthn_credentials_person, idx_recovery_codes_person — present in the live DB but absent from __table_args__. Both reproduced; the baseline diffs byte-faithful against live (all 3 tables identical in columns/constraints/indexes).
3e — two soft Loomworks-isms kept for engine compat; noted forward for P7-2/3 polish.
The standalone reads the KEK from the LOOMWORKS_SECRET_KEY env var (callers inject secret_key explicitly, so this is only the fallback default), and the session cookie name stays loomworks_session (a functional value the engine's host set/clear depends on). Both are STELE-native-rename candidates for P7-2/3 — left as the engine's names so the engine's deployment env and the engine re-point work unchanged.
pyproject.toml into a fresh venv: stele 0.1.0 + all deps at the pinned versions. Zero DUNIN7/Loomworks dependency.alembic upgrade head builds the 3 tables on a fresh DB (stele_verify, since dropped); down/up reversible.playground_dev — all 3 tables identical (cols/constraints/indexes).totp_secret, read back, decrypt KEK-direct, match; bare Fernet token, no data_encryption_keys.stele.* package and loomworks.stele.actor both resolve; 0 residual loomworks.stele.<moved-module> references.DUNIN7/stele (private) main 0d5e265 — the standalone library + manifest + migration baseline; clone-and-run verified.loomworks-engine main ff0c4cf (pushed) — consumes stele as an editable path package; actor.py host-side; 8 local copies removed; suite green.me_security/auth_dev shape); signup/login stay host compositions; the reference app demonstrates the wiring. (CR-2026-115.)COOKIE_NAME (loomworks_session), the host _clear_session_cookie, and api/deps.py session resolution; plus the queued direction loomworks-queued-direction-p7-2-session-delivery-ui-vs-agent-v0_1 (cookie vs bearer over the same token).../stele editable path dep with a version pin against a published stele (and the deploy-time co-location implication, §3c).LOOMWORKS_SECRET_KEY → a STELE env var; loomworks_session → a STELE cookie name (§3e). P7-2/3 polish.__table_args__; only the live DDL had them. (CR-B's "an inventory's lenses define its blind spots," reproduced.)DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Phase 7 P7-1 (Packaging Floor) Completion Record — v0.1 — 2026-06-19