DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/phase-28-expandable-explanations/phase-28-cr-expandable-explanations-v0_1.md

DUNIN7-M4 — INFRASTRUCTURE CHANGE REQUEST

CR-2026-041 — Phase 28: Expandable Explanations from Memory (v0.1)

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.


Contents


1. Executive summary

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


2. Grounding

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.


3. Prerequisites

3.1 Baseline

3.2 Existing assertions

Four room explanation assertions already exist in Memory, committed:

No DST or DRT explanation assertions exist yet.


4. Step 0 — substrate: explains vocabulary, query endpoint, room relationships

Step 0 delivers all substrate changes: the vocabulary addition, the concept convention, the query endpoint, relationships for the four existing room assertions, and tests.

4.1 CR archival

Archive this CR to docs/phase-crs/phase-28-cr-expandable-explanations-v0_1.md.

4.2 Vocabulary addition

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.

4.3 Concept identifier accommodation

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.

4.4 Query endpoint

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.

4.5 Room relationships

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

4.6 Substrate tests

Commit (substrate repo): Phase 28 step 0: explains vocabulary, query endpoint, room relationships.


5. The explains relationship type

Semantics: "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.


6. Concept identifier convention

concept:{type}:{identifier} — three segments, colon-separated.

Type values:

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:

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


7. Query endpoint

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.


8. Room explanation toggles — live from Memory

8.1 Memory room

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.

8.2 Manifestation room

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.

8.3 Shaping room

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

8.4 Rendering room

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


9. "What is this?" on every declared-type card

9.1 Change from current behavior

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.

9.2 Positioning

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.

9.3 Query and fallback

Each card constructs its concept identifier from its 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.

9.4 Toggle behavior

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


10. Reusable explanation components

10.1 RoomExplanationToggle

A reusable component used by all four rooms. Props:

Fetches from the explanations endpoint on mount. Renders the assertion text or fallback. Handles first-visit expansion logic (§12).

10.2 TypeExplanationToggle

A reusable component used by DST and DRT cards. Props:

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.


11. Fallback discipline

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.


12. First-visit expansion behavior

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.


13. Operational: contributing eighteen explanation assertions

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.

13.1 Five DST explanation assertions

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.

13.2 Thirteen DRT explanation assertions

One per DeclaredRenderType. Each explains what the render type produces, in what format, and for whom. Same contribution pattern.

13.3 Assertion numbering

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.


14. Relationship creation script

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:

  1. Queries the Loomworks engagement for all committed assertions.
  2. Identifies the eighteen new explanation assertions (by content match, display number, or a convention the Operator and CC agree on — e.g., the Operator tags each assertion with a recognizable prefix or CC identifies them by contribution timestamp range).
  3. Queries the engagement's DeclaredShapeTypes and DeclaredRenderTypes to get their object_ids.
  4. Creates one 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.


15. Brand compliance

No new visual patterns. Existing toggle styling applies:

Toggle label: {typography.body} with {colors.brass}. Expanded text: {typography.body} with {colors.ink-faint}. "What is this?" link: {typography.caption} with {colors.brass}.


16. Component tests

16.1 Substrate tests (Step 0)

Covered in §4.6.

16.2 Frontend tests (Step 3)

Existing component tests for Shaping and Rendering rooms are updated to reflect the live-query pattern (mock the endpoint response).


17. Order of operations (steps with checkpoints)

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.

  1. Archive this CR to docs/phase-crs/phase-28-cr-expandable-explanations-v0_1.md.
  2. Add explains to relationship vocabulary (§5).
  3. Determine concept-identifier accommodation mechanism (§4.3).
  4. Implement query endpoint GET /engagements/{eid}/explanations/{concept} (§7).
  5. Run room-relationship script — four relationships for assertions #1, #13, #14, #15 (§4.5).
  6. Write substrate tests (§4.6).
  7. Full substrate test suite green.

Commit (substrate repo): Phase 28 step 0: explains vocabulary, query endpoint, room relationships.

Step 1 — Frontend: reusable components, room toggle upgrades, proxy route.

  1. Add proxy route for explanations endpoint (§7).
  2. Create RoomExplanationToggle component (§10.1) with live query, fallback, first-visit expansion.
  3. Upgrade Shaping room: replace static WhatIsShapingToggle with RoomExplanationToggle querying concept:room:shaping.
  4. Upgrade Rendering room: replace static WhatIsRenderingToggle with RoomExplanationToggle querying concept:room:rendering.
  5. Add RoomExplanationToggle to Memory room, querying concept:room:memory.
  6. Add RoomExplanationToggle to Manifestation room, querying 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.

  1. Create TypeExplanationToggle component (§10.2) with live query, consumer_declaration fallback.
  2. Add "What is this?" to every DST card in the Shaping room (§9.2). Remove the empty-card-only condition.
  3. Add "What is this?" to every DRT card in the Rendering room (§9.2). Remove the empty-card-only condition.

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.

  1. Run the DST/DRT relationship script (§14) linking the eighteen new assertions to their concept identifiers.
  2. Verify that all eighteen "What is this?" toggles now show live assertion text.
  3. Write/update frontend component tests (§16.2).
  4. Frontend verification: lint + tsc + build + test clean.
  5. Create 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.


18. Acceptance gate

Phase 28 is accepted when:

  1. Substrate: all tests pass (1170+, 2 skips). explains vocabulary accepted.
  2. Query endpoint returns correct assertion for known concepts.
  3. Query endpoint returns 404 for unknown concepts.
  4. Four room relationships exist (assertions #1, #13, #14, #15 linked to room concepts).
  5. Eighteen DST/DRT relationships exist (assertions #16–#33 linked to type concepts).
  6. Frontend: lint + tsc + build + test clean.
  7. Memory room shows "What is memory?" toggle reading from assertion #1.
  8. Manifestation room shows "What is manifestation?" toggle reading from assertion #13.
  9. Shaping room shows "What is shaping?" toggle reading from assertion #14 (live, no longer static).
  10. Rendering room shows "What is rendering?" toggle reading from assertion #15 (live, no longer static).
  11. First-visit expansion works (toggle expanded on first visit, collapsed on subsequent).
  12. Every DST card (5) shows "What is this?" — including cards with active Shapings.
  13. Every DRT card (13) shows "What is this?" — including cards with active Renders.
  14. DST/DRT toggles show live assertion text from Memory.
  15. Fallback discipline: if an explanation assertion were retracted, the toggle would revert to static text / consumer_declaration.
  16. Component tests cover room toggles (live and fallback), type toggles (live and fallback), first-visit expansion, toggle interaction.
  17. Brand: toggle styling matches existing rooms. No new visual patterns introduced.
  18. All existing Shaping and Rendering room tests pass with mock updates.

On acceptance: tag both repos as phase-28-expandable-explanations. Write implementation notes.


19. Post-CR state


20. What this CR does not specify


21. Kickoff prompt for the Claude Code session


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