Version. 0.1
Date. 2026-06-15
Author. Marvin Percival (DUNIN7 Operator) with Claude.ai (scoping) on DUNIN7-M4.
Status. Opening handoff for a fresh chat to scope and run CR 6b — the Person-view split. Phase 6a is closed (merged, recorded, branch retired). 6b is the next slice. Markdown primary (technical consumer / next-session orientation).
What 6b is. Split Stele's fused Person view into a pure Principal view (identity/auth only) + a separate host view (the host columns), and re-home the ~150 sites across the engine that read host data off the Person object. This is the work 6a deliberately deferred when an inspection found the Person view was a whole-person view, not a principal view.
Why it's its own CR. The host-read re-home is a view-contract change touching ~150 sites — large enough that 6a banked the physical table split behind a clean merge first, and 6b does the view split on its own. The Operator chose this 6a/6b split during 6a (decision 4d in the 6a completion record).
The mechanism is already chosen: model-layer rename-to-find — remove a field from the principal view, the suite breaks every reader loudly, re-point each to the host view, green, commit, per module. The DB columns and the two mirror triggers stay untouched throughout (6b is application-layer; no migration).
What 6b does NOT do: drop any persons column, re-target the 12 host FKs, or retire the mirror triggers — all of that is the contract phase, after 6b. 6b is still inside expand.
In order, from loomworks-record (record.dunin7.com, behind Cloudflare Access — if a file can't be reached directly, ask CC to fetch it):
loomworks-candidate-seed-vN_M in candidate-seeds/loomworks/ (canonical v0.12). The foundation document.current-status-manifest-v0_60.md in current-status/ (just filed; confirm it's still highest before treating as current). It marks 6a complete and names 6b as the next slice.phases/stele-extraction-phase-6a/loomworks-stele-phase-6a-completion-record-v0_1.md. The carry-forwards (§7), the two mirror triggers and their ordered-pair retirement (§5), and the three stele sites that are 6b's starting inventory.standing-notes/loomworks-stele-body-of-work-brief-v0_4.md. Where 6b sits in the arc (Phase 6 = physical split [6a done, 6b ahead]; Phase 7 = de-engine packaging).
| Ref | State |
|---|---|
| engine origin/main | def8b04 (Phase 6a merged) |
| alembic | 0086 (dir + playground_dev + playground_test) |
| suite | 2933 / 46 / 0 |
| branches | no open Stele branch; 6a branch deleted local + remote |
| in flight | nothing |
The new chat starts clean from main at def8b04.
principals (Stele's own Base stele/base.py) — the 7 identity/auth columns; the 2 credential FKs point inward at principals.id; zero outward FK.host_account (host Base) — PK id = principal id (shared id, deferred FK); the 16 host columns.persons — unchanged, all 23 columns; still the source of writes.persons (0085 → principals, 0086 → host_account) keep both new tables in sync with every persons write. 6b must not disturb these — they stay through contract.persons/PersonRow today (6a re-homed nothing). 6b is the re-home.
The seam (from the 6a Step 3.1 inspection): Stele's Person Pydantic view (stele/registry.py) carries six host columns — email, mobile, companion_name, personal_engagement_id, exhaustion_preference, spend_pause — alongside the principal fields. Every Stele resolver returning a Person hands these to callers, so the ~150-site host blast radius flows through this object. Re-homing those reads is the Person-view split.
Starting inventory — the three stele sites:
stele/registry.py — the Person view itself (carries the six host columns; _row_to_person populates it off PersonRow). The central one.stele/registry.py get_person_by_email — resolves a person by the email host column (a host-account query after the split).stele/actor.py actor_from_companion — builds the Companion's ActorRef label from companion_name.
The mechanism (locked in 6a as decision 4c): model-layer rename-to-find. Remove a host field from the principal view / PersonRow mapping; the suite breaks every reader of it loudly (AttributeError / mapper error); re-point each to the host view; green; commit; per module (or per field-group). The DB columns and triggers stay untouched — this is application-layer only.
Open scoping questions for the new chat (do NOT pre-decide here — these are 6b's Step 0 work):
Principal view (id + principal fields) plus a separate host view (the host fields) — and does anything still need a combined object, or do all callers split cleanly into "wants identity" vs "wants host data"?get_person_by_email: does the email lookup move host-side (it's a host-account query now), or stay in Stele reading host_account.email across the join? (Lean to surface, not lock: email lookup is a host concern after the split — but ground it against the real callers first.)actor_from_companion: source the Companion label from host_account.companion_name across the join, or move the actor-label construction host-side?Person view, _row_to_person, the real caller sites, and grep the guard suite for anything policing view shape / model changes.Person-view split is itself the product of one such inspection; expect more inside 6b.playground_dev, not the suite alone — the model-attribute removal catches ORM readers; raw-SQL readers (rare for these columns) only fail when run, so drive the real routes per re-homed module.persons's now-duplicate columns, re-target the 12 host FKs to host_account, retire the two mirror triggers as an ordered pair (they fire in name-sort order; retire them so one never fires without the other — 6a completion record §5). Its own CR. Also where the mint host-column cleanup lands (mint stops writing email/mobile/onboarding_complete to persons).DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — Stele Extraction Phase 6b Opening Handoff — v0.1 — 2026-06-15