Version. 0.1
Date. 2026-04-30
Author. Marvin Percival (DUNIN7), prepared via Claude.
Target. /Users/dunin7/loomworks (substrate) and /Users/dunin7/loomworks-ui (frontend). DUNIN7-M4 (MacMini).
Priority. Standard.
Confidential. Internal.
Companion to. Phase 28 scoping note v0.1, room-explanations-and-expandable-scope-v0_1, Phase 3 CR v0.2 (relationship infrastructure), Phase 23 CR v0.1 (Shaping room toggle), Phase 27 CR v0.1 (Rendering room toggle), methodology v0.19.
Status. First draft. Eight scoping decisions (S1–S8). No open design questions.
explains vocabulary, query endpoint, room relationshipsexplains relationship type
Phase 28 connects the explanation toggles to Memory. Every methodology room and every declared-type card reads its explanation from a designated assertion via the new explains relationship type, falling back to static text when no assertion exists. The substrate gains the explains vocabulary entry, a concept-identifier convention, and a query endpoint. The frontend replaces static toggle text with live queries in all four rooms, and places "What is this?" on every DeclaredShapeType and DeclaredRenderType card (not just empty ones). Eighteen new explanation assertions (five DST, thirteen DRT) are contributed through the product surface and linked to their concepts.
This resolves Residue 22 (expandable room explanations reading from Memory).
Layer 1 — Methodology document v0.19. Each room in the pipeline has a distinct purpose. Explanations of those purposes belong in the engagement's own Memory — the system explains itself through the same knowledge structure it manages for external work.
Layer 2 — Room explanations scope v0.1. The original specification. Room-level expandable sections reading from explains assertions. Scaling to declared types. First-visit expansion. Fallback discipline.
Layer 3 — Phase 3 CR v0.2. The relationship infrastructure. Relationship model, add_relationship function, relationship_added events. Six vocabulary types. Phase 28 adds a seventh.
Layer 4 — Phase 23 CR v0.1 and Phase 27 CR v0.1. The existing toggle UI in Shaping and Rendering rooms with static text. These become live.
phase-27-rendering-room-redesign. 1170 tests, 2 skips.phase-27-rendering-room-redesign. 124 component tests, 6 E2E (1 skipped). Lint + tsc + build + test clean. Fourteen surfaces.Four room explanation assertions already exist in Memory, committed:
No DST or DRT explanation assertions exist yet.
explains vocabulary, query endpoint, room relationshipsStep 0 delivers all substrate changes: the vocabulary addition, the concept convention, the query endpoint, relationships for the four existing room assertions, and tests.
Archive this CR to docs/phase-crs/phase-28-cr-expandable-explanations-v0_1.md.
Add "explains" to the Relationship.vocabulary literal union. The union becomes seven values:
vocabulary: Literal[
"wasDerivedFrom",
"wasRevisionOf",
"wasInvalidatedBy",
"wasAttributedTo",
"wasGeneratedBy",
"wasConsideredAndRejected",
"explains",
]
Update the same literal in the add_relationship function signature.
The Relationship.target field is typed as MemoryRef. For explains relationships, the target is a concept identifier string (e.g., concept:room:shaping), not a reference to a memory object. CC determines the cleanest mechanism to accommodate this:
Option A — qualifier carries the concept. The target is a self-referencing sentinel (e.g., same as source), and the qualifier dict carries {"concept": "concept:room:shaping"}. The query endpoint reads from qualifier.
Option B — string-compatible target. The MemoryRef type already accommodates string identifiers. The concept string goes directly in target.
Option C — dedicated field. A concept_target field on Relationship, used only for explains. Other vocabulary types ignore it.
The requirement is: the query works, the concept is discoverable, and no existing relationship behavior breaks. CC picks the option that requires the least disruption. The CR does not prescribe the mechanism — it prescribes the behavior.
GET /engagements/{eid}/explanations/{concept} where {concept} is the full concept identifier string (URL-encoded if necessary, e.g., concept:room:shaping).
Response (200): the assertion that explains this concept. Full assertion content: assertion_id, display_number, content (text), contributor_display_name, committed_at timestamp, version.
Response (404): no explains relationship exists for this concept in this engagement.
Resolution logic: find all explains relationships where the concept matches the target (or qualifier, per §4.3). Resolve each relationship's source to its assertion. Filter to committed assertions. Return the most recent by committed timestamp. If multiple assertions explain the same concept (e.g., after an assertion is revised and a new explains relationship is created for the revised version), the most recently committed wins.
Create four explains relationships linking the existing room assertions to their concept identifiers:
| Assertion | Concept identifier |
|-----------|-------------------|
| #1 (What Memory is) | concept:room:memory |
| #13 (What a Manifestation is) | concept:room:manifestation |
| #14 (What Shaping is) | concept:room:shaping |
| #15 (What Rendering is) | concept:room:rendering |
CC writes an idempotent script (following the pattern of scripts/amend_loomworks_dst_drt_abbreviations.py) that creates these four relationships. The script runs against the live Loomworks engagement. Each relationship is a standard add_relationship call with vocabulary "explains".
explains is accepted as a vocabulary value in add_relationship.explains relationships exist for the same concept, the most recently committed assertion wins.
Commit (substrate repo): Phase 28 step 0: explains vocabulary, query endpoint, room relationships.
explains relationship typeSemantics: "This assertion explains this concept." The source is an assertion. The target is a concept identifier. The relationship is a standard memory object with full provenance — who created it, when, in which engagement.
An assertion can explain multiple concepts (one relationship per concept). A concept can have multiple explains relationships (the query endpoint resolves to the most recent committed assertion). A revised assertion needs a new explains relationship — the old relationship still points at the old assertion version, but the query returns the revision.
The explains relationship does not appear in PROV. It is a Loomworks addition, like wasConsideredAndRejected is a Loom Protocol addition. If this ever needs to be registered in the Loom Protocol vocabulary, it can be — but for now it is a Loomworks-level convention.
concept:{type}:{identifier} — three segments, colon-separated.
Type values:
room — a methodology room.dst — a DeclaredShapeType.drt — a DeclaredRenderType.
Room identifiers: memory, manifestation, shaping, rendering.
DST and DRT identifiers: the object's object_id (UUID) from the substrate. This is stable — object IDs do not change when names are amended.
Full examples:
concept:room:renderingconcept:dst:550e8400-e29b-41d4-a716-446655440001concept:drt:550e8400-e29b-41d4-a716-446655440002The convention is not enforced by schema — it is a naming pattern. The query endpoint treats the concept as an opaque string. The frontend constructs concept identifiers from known room names and object IDs.
Detailed in §4.4. The endpoint is a read-only convenience join over relationships and assertions. The proxy route on the frontend:
/api/engagements/[eid]/explanations/[concept] → proxies to substrate GET /engagements/{eid}/explanations/{concept}.
The frontend calls this route with the concept identifier and receives the assertion text or a 404. On 404, the component falls back to static text or consumer_declaration.
Gains "What is memory?" expandable toggle between room header and assertion list. New UI element. Queries concept:room:memory. Falls back to static text:
> Accumulated engagement knowledge. Each assertion carries who contributed it, when, and how.
Gains "What is manifestation?" expandable toggle between room header and manifestation content. New UI element. Queries concept:room:manifestation. Falls back to static text:
> A snapshot of Memory at a moment in time. Downstream work — Shaping, Rendering — builds against this snapshot.
Existing "What is shaping?" toggle (Phase 23) switches from static text to live query against concept:room:shaping. Falls back to current static text (derived from assertion #14).
Existing "What is rendering?" toggle (Phase 27) switches from static text to live query against concept:room:rendering. Falls back to current static text (derived from assertion #15).
Currently, "What is this?" appears only on empty cards (no Shapings / no Renders). Phase 28 places it on every card — the explanation is about the type itself, independent of whether work exists within it.
On DST cards (Shaping room, expanded state): "What is this?" toggle positioned after Shaping rows (or after the empty-state message). Below the action row.
On DRT cards (Rendering room, expanded state): "What is this?" toggle positioned after Render rows, retired/invalidated collapsibles, and the action row. At the bottom of the expanded card body.
Each card constructs its concept identifier from its object_id:
concept:dst:{object_id}concept:drt:{object_id}Queries the explanations endpoint. On 200, displays the assertion text. On 404, falls back to the consumer_declaration field from the declared type.
Same as existing "What is this?" toggles — collapsed by default, expands on click, collapses on second click. Text link in {typography.caption} with {colors.brass}. Expanded text in {typography.body} with {colors.ink-faint}.
A reusable component used by all four rooms. Props:
conceptId: string — the concept identifier to query.roomName: string — used for the toggle label ("What is [roomName]?").fallbackText: string — static text to display on 404.Fetches from the explanations endpoint on mount. Renders the assertion text or fallback. Handles first-visit expansion logic (§12).
A reusable component used by DST and DRT cards. Props:
conceptId: string — the concept identifier to query.fallbackText: string — consumer_declaration text to display on 404.Fetches from the explanations endpoint on mount. Renders "What is this?" toggle with assertion text or fallback.
CC may unify these into a single component with a label prop if the implementations are close enough. The requirement is: both patterns work with correct query, fallback, and styling.
The frontend never breaks when explanations are absent.
| Context | Fallback | Condition |
|---------|----------|-----------|
| Room toggle | Static subtitle text (hardcoded) | No explains assertion for concept:room:X |
| DST card | consumer_declaration field | No explains assertion for concept:dst:X |
| DRT card | consumer_declaration field | No explains assertion for concept:drt:X |
If an explains assertion is later retracted, the query endpoint returns 404 (retracted assertions are excluded), and the fallback reappears. No special handling needed — the query does the right thing.
Room toggles only. On the Operator's first visit to a room (no localStorage entry for that room), the explanation toggle starts expanded. After the Operator collapses it, localStorage remembers the state. On subsequent visits, the toggle is collapsed.
localStorage key pattern: loomworks:explanation:{concept}:collapsed — boolean. Absent = first visit (expanded). Present + true = collapsed.
"What is this?" on cards: no first-visit logic. Always collapsed by default. The card is already an expand-to-reveal pattern; the explanation within it is a secondary toggle. Expanding the card is the primary disclosure; the explanation toggle is opt-in.
The Operator contributes eighteen explanation assertions through the Memory room UI during this phase. These are real assertions — contributed, committed, and linked to their concepts.
One per DeclaredShapeType. Each explains what the shape type is, what it produces, and who consumes it. The Operator writes the assertion text and contributes through the Memory room contribution surface.
The assertion text should follow the voice established by the four room explanation assertions (#1, #13, #14, #15) — plain language, concrete examples, no jargon. CC does not write these assertions. The Operator contributes them.
One per DeclaredRenderType. Each explains what the render type produces, in what format, and for whom. Same contribution pattern.
After contribution, the eighteen assertions will be numbered #16 through #33 (assuming no other assertions are contributed between now and then). The exact numbering depends on contribution order. The CR does not prescribe numbering — the substrate assigns display numbers.
After the Operator contributes and commits the eighteen assertions, CC runs a script that creates the explains relationships linking each assertion to its concept identifier.
The script:
explains relationship per assertion, linking each to its concept:dst:{object_id} or concept:drt:{object_id}.
The script is idempotent — running it twice does not create duplicate relationships (checks for existing explains relationships before creating).
Pattern: same as scripts/amend_loomworks_dst_drt_abbreviations.py.
No new visual patterns. Existing toggle styling applies:
env-memory (#E4E6E8) outer frame.env-manifestation (#DCE3DB) outer frame.env-shaping (#E6D4C5) outer frame (unchanged).env-rendering (#E6DCCA) outer frame (unchanged).
Toggle label: {typography.body} with {colors.brass}.
Expanded text: {typography.body} with {colors.ink-faint}.
"What is this?" link: {typography.caption} with {colors.brass}.
Covered in §4.6.
Existing component tests for Shaping and Rendering rooms are updated to reflect the live-query pattern (mock the endpoint response).
Auto-mode posture: Step 0 auto. Steps 1–2 auto, Checkpoint A. Step 3 pauses for Operator contribution. Steps 4–5 auto, Checkpoint B (final).
Step 0 — Substrate: vocabulary, endpoint, room relationships, tests.
docs/phase-crs/phase-28-cr-expandable-explanations-v0_1.md.explains to relationship vocabulary (§5).GET /engagements/{eid}/explanations/{concept} (§7).
Commit (substrate repo): Phase 28 step 0: explains vocabulary, query endpoint, room relationships.
Step 1 — Frontend: reusable components, room toggle upgrades, proxy route.
concept:room:shaping.concept:room:rendering.concept:room:memory.concept:room:manifestation.Verification: lint + tsc + build clean. All four rooms show explanation toggles. Shaping and Rendering show live assertion text (room relationships exist). Memory and Manifestation show live assertion text (room relationships exist).
Commit (frontend repo): Phase 28 step 1: room explanation toggles from Memory.
Step 2 — Frontend: "What is this?" on every card.
Verification: lint + tsc + build clean. "What is this?" appears on every card. DST/DRT toggles show consumer_declaration fallback (no explanation assertions exist yet).
Commit (frontend repo): Phase 28 step 2: explanation toggles on all declared-type cards.
Checkpoint A — UI functional. Room toggles read live from Memory. Card toggles fall back to consumer_declaration. Operator confirms before contributing assertions.
Step 3 — Operational pause: Operator contributes eighteen assertions.
The Operator contributes five DST and thirteen DRT explanation assertions through the Memory room UI. Each is committed. This step happens outside CC — CC halts and waits.
When the Operator signals that all eighteen assertions are committed, CC proceeds.
Step 4 — Relationship script, component tests, implementation notes.
docs/phase-impl-notes/phase-28-implementation-notes-v0_1.md.
Commit (substrate repo, relationship script output): Phase 28 step 4: DST/DRT explanation relationships.
Commit (frontend repo): Phase 28 step 4: component tests.
Commit (substrate repo): Phase 28 step 4: implementation notes.
Checkpoint B — Final. Both repos green. Tag both repos as phase-28-expandable-explanations.
Phase 28 is accepted when:
explains vocabulary accepted.
On acceptance: tag both repos as phase-28-expandable-explanations. Write implementation notes.
explains vocabulary. Query endpoint. Twenty-two explains relationships (4 room + 5 DST + 13 DRT).explains relationships.explains relationship for the revised version. Phase 29 (Memory Room Revision UI) makes this easier.
Read the Change Request document at the path I supply below. This is
CR-2026-041 v0.1, the Phase 28 Change Request. You are the executing
agent named in the CR.
CR path: ~/Downloads/phase-28-cr-expandable-explanations-v0_1.md
Phase 28 connects explanation toggles to Memory. The substrate gains
the "explains" relationship vocabulary entry, a concept-identifier
convention, and a query endpoint. The frontend replaces static toggle
text with live queries in all four rooms, and places "What is this?"
on every declared-type card.
Key requirements:
- Add "explains" to Relationship.vocabulary (seventh value).
- Concept identifiers: concept:room:{name}, concept:dst:{id},
concept:drt:{id}. Convention strings, not memory objects.
- Query endpoint: GET /engagements/{eid}/explanations/{concept}.
Returns assertion content or 404. Most recent committed wins.
- Determine the cleanest mechanism for concept-identifier targets
on Relationship (qualifier, string-compatible target, or
dedicated field). See CR §4.3 for options.
- Four room relationships created by script (assertions #1, #13,
#14, #15).
- RoomExplanationToggle: reusable component for all four rooms.
Live query, fallback to static text, first-visit expansion via
localStorage.
- TypeExplanationToggle: reusable component for DST/DRT cards.
Live query, fallback to consumer_declaration.
- "What is this?" on EVERY card — not just empty ones.
- Proxy route for explanations endpoint.
Substrate baseline: 1170 tests, 2 skips.
Frontend baseline: 124 component tests, 6 E2E (1 skip).
Run Step 0: substrate changes + CR archival.
Steps 1–2 auto, Checkpoint A halts for Operator contribution.
After Operator signals assertions committed, Steps 4–5 auto,
Checkpoint B halts for tagging.
Brand: no new visual patterns. Existing toggle styling applies.
Room environment tints unchanged.
Implementation notes at Step 4:
docs/phase-impl-notes/phase-28-implementation-notes-v0_1.md
DUNIN7 — Done In Seven LLC — Miami, Florida Phase 28: Expandable Explanations from Memory — CR v0.1 — 2026-04-30