Version. 0.1
Date. 2026-05-03
CR number. CR-2026-045
Provenance. Claude.ai CR drafting session. Operator: Marvin Percival.
Status. Working draft. Awaiting Operator approval.
Scoping note. loomworks-activity-observability-scoping-note-v0_1.md (2026-05-02).
The engagement has no central view of what's in flight. To discover that a shape is producing or a render is running, the Operator must open each room, expand each card, and look for status messages. This is untenable with real activity across three rooms.
This CR adds engagement activity observability in three layers:
Layer 1 — Room tab activity indicators. The RoomSwitcher gains pulsing dots: green for running jobs, amber for items awaiting Operator action. One new substrate endpoint, one 10-second poll.
Layer 2 — Room-level activity banners. Inside each room, above content, a banner names running and pending items with elapsed time. Links to or scrolls to the relevant card. Collapses to nothing when idle.
Layer 3 — Event detail drill-down. Click any shape event card, render event card, or assertion to expand a detail panel showing its full birth certificate — trigger, lineage, versions, content, state transitions.
From scoping note v0.1:
N1 — Engagement-scoped single endpoint (Position A). One activity-summary endpoint returns counts for all rooms. The RoomSwitcher needs all rooms' data. One poll, not four.
N2 — 10-second polling interval. Responsive enough for "is something running" without hammering the server.
N3 — Inline expansion (Position A). Detail panels expand inline on click, consistent with existing "Show content" and "Shaping details" patterns. Not separate pages.
N4 — Phase number. This is Phase 33.
Add to the engagements router:
GET /engagements/{eid}/activity-summary
Response:
{
"shaping": { "running": 2, "pending_confirmation": 9 },
"rendering": { "running": 1, "pending_action": 0 },
"memory": { "pending_commit": 3 }
}
Query logic:
shaping.running — count of shaping_jobs with status IN ('queued', 'dispatched') for this engagement.shaping.pending_confirmation — count of shape events in pending state (unconfirmed shapes awaiting Operator review).rendering.running — count of render_jobs with status IN ('queued', 'dispatched') for this engagement.rendering.pending_action — reserved for future use. Return 0.memory.pending_commit — count of assertions in held state for this engagement.The endpoint reads existing tables and views. No new migrations. No new MemoryObject types.
Response schema: ActivitySummaryResponse with nested RoomActivitySchema per room.
The detail panels specified in Layer 3 require fields that may not be on current response schemas. CC audits at pre-flight and adds any missing fields.
Shape event response — check and add if missing:
selected_memory_refs — list of version-pinned memory references the shaping agent selected.excluded_but_considered — list of memory references considered but excluded, with reasons.seed_version_at_production — the seed version when this shape was produced.engagement_version_at_production — the engagement version when this shape was produced.selection_criterion — from the declared shape type.trigger — what triggered this production (contributor_request / agent_request).triggered_by — actor reference for who/what triggered it.Render event response — check and add if missing:
trigger — what triggered this production (explicit_request / declared_auto).triggered_by — actor reference for who/what triggered it.Assertion response — check and add if missing:
contribution_mode — text / voice / image / pdf.CC should audit each response schema against the corresponding detail panel field list (§5.3) and add any fields that are stored but not surfaced. Fields that are not stored at all should be noted in the implementation notes as future additions, not invented.
New test file tests/test_activity_summary.py:
queued. Verify shaping.running == 1.shaping.pending_confirmation == 1.queued. Verify rendering.running == 1.memory.pending_commit == 1.8 tests.
Create a useActivitySummary hook:
GET /engagements/{eid}/activity-summary on mount and every 10 seconds.{ shaping, rendering, memory, isLoading }.
The RoomSwitcher component (the tab bar showing Memory 14 · Manifestation v1 · Shaping 3 · Rendering 10) consumes useActivitySummary.
Pulsing green dot: appears next to the count badge on a room tab when that room has running > 0. CSS animation: opacity pulse between 1.0 and 0.4 on a 1.5-second cycle. The dot is small (6px diameter), positioned to the right of the room's existing count badge.
Static amber dot: appears next to the count badge when the room has items awaiting Operator action (pending_confirmation > 0 for Shaping, pending_commit > 0 for Memory). Amber dot takes precedence visually only when there is no green dot — if both running and pending, show the green pulsing dot (running is the higher-priority signal).
No dot: when all counts for a room are zero.
Manifestation room: no activity indicator. Manifestation derivation is synchronous (no background jobs).
Each room gains a banner component rendered above the room's content, below the room heading (and below the room explanation toggle if present). The banner is a light-background strip that shows running and pending items.
When empty: the banner component renders nothing (no empty-state message, no placeholder).
Banner ground: var(--lw-color-background-subtle) or the room's environment tint at low opacity, with left border accent in the room tint color. Caption-weight type (Inter, 13px).
Shaping room banner:
Derives from the room's existing shape and shaping job data. Two sections:
1 shape producing — "Goosey Finds the Lost Duckling" via lost-duckling-story-pages · started 45s ago.9 shapes awaiting confirmation across 2 shapings.Each named item links to or scrolls to the relevant card.
Rendering room banner:
Derives from the room's existing render and render job data. One section:
1 render producing — "Animated Story AAA" · Storybook rule specialist · started 2 min ago.Memory room banner:
Derives from the room's existing assertion data. One section:
The banners derive from data already fetched by each room page. No additional API calls. The activity-summary endpoint (Layer 1) provides the counts for the tab dots; the room's own data provides the names and detail for the banners.
Clicking a shape event card, render event card, or assertion card expands a detail panel below the card's existing content. The panel uses the same inline-expansion pattern as "Show content" and "Shaping details" — click to expand, click to collapse, pushing cards below down.
The detail panel is separate from the existing "Show content" toggle. Both can be open simultaneously. The detail toggle label is "Details" (or "Birth certificate" if the Operator prefers — CC should use "Details" as the default label).
Consistent two-column label/value layout across all three panel types:
font-size: 13px, color: var(--lw-color-ink-faint)).8px vertical spacing.
Panel background: var(--lw-color-background-subtle) (same as banner). Left border accent in room tint.
| Field | Label | Value source |
|---|---|---|
| Title | Title | title (Operator-entered or auto-derived) |
| State | State | state with colored dot (green=confirmed, amber=pending, gray=retired) |
| Produced at | Produced | produced_at formatted timestamp |
| Triggered by | Triggered by | triggered_by actor display_name and kind |
| Trigger type | Trigger | trigger value |
| Against Manifestation | Manifestation | manifestation_object_id · version manifestation_version |
| Shaping | Shaping | shaping slug · version shaping_version |
| Executor | Executor | agent display_name · instruction version |
| Seed version | Seed version | seed_version_at_production |
| Engagement version | Engagement version | engagement_version_at_production |
| Selection criterion | Selection | selection_criterion |
| Selected memory refs | Selected memory | List with display labels |
| Excluded but considered | Excluded | List with reasons (collapsible if >3 items) |
| Confirmation | Confirmation | who confirmed, when, terminal state |
| Content | expandable | produced_shape_content in MarkdownPanel |
Fields that are null or absent on the response: omit the row entirely. Do not show empty rows.
| Field | Label | Value source |
|---|---|---|
| Source shape | Source shape | source_shape_title with shape event reference |
| Render type | Render type | name and consumer declaration |
| Format | Format | render_format badge |
| Specialist | Specialist | name · instruction version |
| Triggered by | Triggered by | actor display_name and kind |
| Trigger type | Trigger | trigger value |
| Produced at | Produced | timestamp |
| State | State | produced / retired / invalidated with colored dot |
| Retirement reason | Reason | if applicable (only shown when retired/invalidated) |
| Content | expandable | render_content in MarkdownPanel |
| Download | button | DOWNLOAD button (reuses existing download infrastructure) |
| Field | Label | Value source |
|---|---|---|
| Display number | # | display_number |
| Content | Content | assertion text (already visible, but included for completeness in panel) |
| State | State | held / committed / redirected with colored dot |
| Contributor | Contributed by | who, when |
| Committer | Committed by | who, when (if committed) |
| Contribution mode | Contributed via | text / voice / image / pdf |
| Relationships | Related to | list of linked concept identifiers |
| Redirect target | Redirected to | engagement name (if redirected) |
Create a shared DetailPanel component that takes a list of { label, value, type? } entries and renders the two-column layout. Each detail panel (shape, render, assertion) composes this shared component with its specific field list. The type field allows special rendering: "badge" for format badges, "dot" for state dots, "list" for memory ref lists, "expandable" for content sections, "button" for action buttons.
shaping.running: 1. Verify green pulsing dot on Shaping tab.memory.pending_commit: 3, memory.running: 0. Verify amber dot on Memory tab.shaping.running: 1, shaping.pending_confirmation: 5. Verify green dot (not amber) on Shaping tab.selected_memory_refs is null. Verify that row is not rendered.14 tests.
Step 0 — Pre-flight and CR archive.
Archive this CR to docs/phase-crs/phase-33-cr-engagement-activity-observability-v0_1.md.
Audit the current response schemas against the detail panel fields in §6.3, §6.4, §6.5:
ShapeEventResponse for: selected_memory_refs, excluded_but_considered, seed_version_at_production, engagement_version_at_production, selection_criterion, trigger, triggered_by.RenderEventResponse for: trigger, triggered_by.AssertionResponse for: contribution_mode, relationship links.Record which fields are present, which need adding, and which are not stored at all (noted for implementation notes, not invented).
Also audit the shaping_jobs and render_jobs tables/views to confirm the status field values and query patterns for the activity-summary endpoint.
Commit: Phase 33 step 0: CR archive and schema audit.
Step 1 — Activity summary endpoint and response schema additions.
Create the activity-summary endpoint per §3.1. Add the ActivitySummaryResponse and RoomActivitySchema schemas. Add any missing response fields identified in Step 0 per §3.2.
Verification: manual curl to the endpoint returns expected shape.
Commit: Phase 33 step 1: activity summary endpoint and schema additions.
Step 2 — Substrate tests.
Write the 8 tests per §3.3.
Verification: uv run pytest -v green. All existing tests still pass.
Commit: Phase 33 step 2: activity summary tests.
Step 3 — Room tab activity indicators (frontend).
Create useActivitySummary hook per §4.1. Update RoomSwitcher with dot indicators per §4.2. CSS for pulsing green dot and static amber dot.
Verification: lint + tsc + build clean.
Commit (frontend repo): Phase 33 step 3: room tab activity indicators.
Checkpoint A — Operator sees dots on room tabs. Produce a shape or trigger a render. Verify pulsing green dot appears on the relevant tab while the job runs. Verify amber dot for pending items.
Step 4 — Room-level activity banners (frontend).
Create banner components for Shaping, Rendering, and Memory rooms per §5. Banners derive from existing room data. No new API calls.
Verification: lint + tsc + build clean.
Commit (frontend repo): Phase 33 step 4: room-level activity banners.
Step 5 — Event detail panels (frontend).
Create shared DetailPanel component per §6.6. Create shape, render, and assertion detail panels per §6.3–§6.5. Add "Details" toggle to each card type. Wire up content expansion within detail panels.
Verification: lint + tsc + build clean.
Commit (frontend repo): Phase 33 step 5: event detail panels.
Step 6 — Frontend tests.
Write the 14 tests per §7. Update any affected existing tests.
Verification: lint + tsc + build + test clean.
Commit (frontend repo): Phase 33 step 6: frontend tests.
Step 7 — Implementation notes.
Write implementation notes to docs/phase-impl-notes/phase-33-implementation-notes-v0_1.md. Record any findings, schema gaps noted but not filled, and construction decisions made during execution.
Commit: Phase 33 step 7: implementation notes.
Checkpoint B — Final. Both repos green. Operator walks the full flow:
On acceptance: tag both repos as phase-33-engagement-activity-observability. Write implementation notes if not done at Step 7.
This CR is accepted when:
dispatched_at is set.
Read the Change Request document at the path I supply below. This is
CR-2026-045 v0.1, the Phase 33 Change Request. You are the executing
agent named in the CR.
CR path: ~/Downloads/phase-33-cr-engagement-activity-observability-v0_1.md
Phase 33 adds engagement activity observability in three layers:
room tab indicators (pulsing dots), room-level activity banners
(named running items), and event detail drill-down (birth certificate
panels on shape, render, and assertion cards).
Key points:
- One new substrate endpoint: activity-summary. Queries existing
tables. No migrations.
- Response schema audit at Step 0 — check shape, render, and
assertion responses for detail panel fields. Add what's stored
but not surfaced. Note what's not stored.
- useActivitySummary hook with 10-second polling.
- RoomSwitcher dots: green pulsing for running, amber for pending.
- Banners derive from existing room data, no new API calls.
- DetailPanel shared component, three specialized panels.
- "Details" toggle on cards, separate from existing "Show content".
Substrate baseline: 1274 tests, 2 skips.
Frontend baseline: 246 vitest, lint + tsc + build + test clean.
Step 0: archive CR + schema audit. Steps 1–3 auto.
Checkpoint A halts for Operator to see dots on tabs.
Steps 4–7 auto. Checkpoint B halts for final smoke-test and tagging.
Implementation notes at Step 7:
docs/phase-impl-notes/phase-33-implementation-notes-v0_1.md
DUNIN7 — Done In Seven LLC — Miami, Florida Phase 33: Engagement Activity Observability — CR v0.1 — 2026-05-03