Skip to content

fix(desktop): restore observer-feed regressions from #1381#1412

Open
wpfleger96 wants to merge 9 commits into
mainfrom
duncan/observer-feed-1381-restore
Open

fix(desktop): restore observer-feed regressions from #1381#1412
wpfleger96 wants to merge 9 commits into
mainfrom
duncan/observer-feed-1381-restore

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Three feed regressions introduced by the #1381 activity-feed rebuild, all confined to desktop UI code.

#1381 restructured the feed around a classifier registry and 12 render classes. In doing so, per-turn prompt context sections were moved behind a modal-only toggle, metadata items lost their semantic titles, permission rows never showed the outcome, and metadata was dropped from the "Now" activity-bar summary scan.

  • Fix 1 — per-turn context inline. PromptUserMessage now renders a PromptContextInline block directly below the user bubble. Each section title is visible and the body expands on click. A "View full" button opens the scrollable modal for focused reading. The TurnSetupFooter context toggle and PromptContextDialog modal-only path are removed.
  • Fix 2 — system-prompt item identity. RawRailActivity now uses item.title as the ActivityRowLabel verb instead of the hard-coded "Captured" string, so "System prompt" and "Prompt context" items render with their real names and section count rather than the anonymous "Captured N raw sections" label.
  • Fix 3 — permission outcome. agentSessionTranscript.ts adds a pendingPermissions map to TranscriptState (JSON-RPC request id → lifecycle item id + option kind map). When session/request_permission is processed the map is populated using a jsonRpcId() helper that accepts both string and finite-number ids via JSON.stringify (preventing collisions between numeric 1 and string "1", and correctly handling the numeric ids the ACP runtime preserves as serde_json::Value). An acp_write with result.outcome and no method correlates by id and appends the outcome: "Approved (allow_once)" / "Denied (reject_once)" / "Cancelled". Unit tests cover string selected, string cancelled, string unmatched id, numeric selected, numeric cancelled, and the numeric/string collision case.
  • Fix 4 — isMeaningfulItem() restored. The blanket metadata → false exclusion is replaced with a targeted check: acpSource === "raw_json_rpc" items (infrastructure noise) remain excluded; all other metadata items (system prompt, prompt context) contribute to the "Now" summary again.

Restores pre-#1381 behavior. Unit/lib CI covers all logic changes. Feed render surfaces (Fixes 1 and 2) are E2E-only and are called out for manual verification.

Closes #1381 regression (not the PR itself — see that PR for original context).

npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 3 commits June 30, 2026 19:49
Three regressions introduced by #1381's activity-feed rebuild:

1. Per-turn prompt context sections are now visible inline in the feed
   body. Previously they were hidden behind a modal accessible only via
   a small CheckCheck toggle inside the user-message bubble footer.
   Now the sections render directly below the user bubble with each
   section title visible and body expandable on click. A "View full"
   button opens the scrollable modal for focused reading.

2. Metadata items (system prompt, prompt context) now display their
   semantic title instead of the anonymous "Captured N raw sections"
   label. RawRailActivity now uses item.title as the verb, so "System
   prompt" and "Prompt context" render with their real names.

3. Permission rows now show the decided outcome. The permission response
   arrives as an acp_write event with result.outcome and the same
   JSON-RPC id as the request. A pendingPermissions map on TranscriptState
   indexes request id → lifecycle item id + option kind map. On the
   response the outcome is appended: Approved (allow_once) /
   Denied (reject_once) / Cancelled.

Also restores metadata participation in isMeaningfulItem() for the Now
summary bar. Raw JSON-RPC frames remain excluded (acpSource=raw_json_rpc);
all semantic metadata (system prompt, prompt context) now contribute
again, matching pre-#1381 behavior.

System-prompt key (system-prompt:${ch}) is intentionally unchanged —
the frame predates session creation and correctly reflects current
channel setup by replacing on each session/new.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…lation

JSON-RPC 2.0 allows both string and numeric ids. The ACP runtime preserves
permission request ids as serde_json::Value for exactly this reason. The
prior commit used asString(payload.id) for both the request index and
response lookup in pendingPermissions, silently dropping any numeric id
and leaving the outcome unattached.

Add a jsonRpcId() helper that accepts string or finite-number values and
keys them via JSON.stringify (preventing collisions between the number 1
and the string "1"). Use it at both the request-index site and the
response-lookup site instead of asString.

Regression tests: numeric id with selected allow_once, numeric id with
cancelled, and a collision test verifying number 1 and string "1" attach
to their respective items independently.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Inside the acp_write response branch, `responseId` is typed
`string | null`. The `if (pending && outcomeKind)` guard only
narrowed `pending`, not `responseId`, so `d.pendingPermissions.delete(responseId)`
was rejected by tsc (argument of type 'string | null' not assignable
to 'string').

Add `&& responseId` to the guard. This is semantically a no-op —
`pending` can only be truthy when the earlier `responseId ?` branch
took the non-null path — but TS requires the explicit narrowing.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the duncan/observer-feed-1381-restore branch from 787713f to ce9a03f Compare June 30, 2026 23:49
…sions

Adds a Playwright smoke spec that seeds observer relay events directly
into the production appendAgentEvent → processTranscriptEvent ingestion
path via a new __BUZZ_E2E_SEED_OBSERVER_EVENTS__ e2e bridge hook, then
screenshots four surfaces restored by PR #1412:

- 01-prompt-context-inline: collapsed inline prompt-context block
- 02-system-prompt-title: system prompt with title visible in feed
- 03-permission-approved: permission row with Approved (allow_once) outcome
- 04-permission-cancelled: permission row with Cancelled outcome

Supporting changes:
- injectObserverEventsForE2E exported from observerRelayStore.ts
- __BUZZ_E2E_SEED_OBSERVER_EVENTS__ Window hook wired in e2eBridge.ts
- spec added to smoke project in playwright.config.ts

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jul 1, 2026
wpfleger96 pushed a commit that referenced this pull request Jul 1, 2026
Store the resolved permission outcome on a new `outcome?` field of the
lifecycle item type instead of appending it to `text` via joinLifecycleText.
This separates the data model cleanly: `text` holds request detail + options;
`outcome` holds the final decision.

LifecycleActivity.tsx renders permission items in three visually distinct parts:
1. Request row: shield icon + title + request description
2. Options sub-line: muted indented 'Options: ...' text
3. Decision row (when resolved): divider + colored icon + outcome label
   - green + CheckCircle2 for 'Approved (...)'
   - destructive + XCircle for 'Denied (...)'
   - muted + XCircle for 'Cancelled'

Also adds expanded E2E screenshot variants:
- 05-prompt-context-expanded.png: all three accordion sections open
- 06-system-prompt-expanded.png: outer <details> + inner section <details> open

Unit tests updated to assert outcome is on item.outcome (not in item.text).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 pushed a commit that referenced this pull request Jul 1, 2026
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 4 commits June 30, 2026 20:57
Adds isSpineItem() predicate — true for tools, messages, thoughts, plans,
and meaningful lifecycle events; false for metadata items (system prompt,
prompt context). These are 'reads' that should recede when real work is
present, per VISION_ACTIVITY.md:49,61 (failures rise; reads recede;
suppression is what makes signal legible).

BotActivityBar now uses a two-tier headline scan: first pass collects
spine-only headlines; if none found (session start / idle), falls back to
all meaningful items via isMeaningfulItem so the bar isn't left empty.

Feed visibility for metadata items is unaffected — AgentSessionTranscriptList
renders them independently of this predicate.

Also fixes the isMeaningfulItem docstring, which incorrectly claimed the
function feeds a 'Now summary' (it only feeds the headline scan).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…dion

RawRailActivity now branches on acpSource: raw_json_rpc items keep the
existing <pre>/details raw treatment (the ambient safety net), while all
other named metadata items (system prompt, steer-turn prompt context)
render via the shared PromptSectionList accordion — the same polished
rounded-2xl card used for per-turn prompt context.

Extract PromptContextSections + PromptContextSectionAccordion from
AgentSessionTranscriptList into PromptSectionAccordion.tsx and import
the shared component back in both AgentSessionTranscriptList and
RawRailActivity. No visual change to prompt-context; the raw-rail
treatment is now reserved for genuine raw payloads only.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
…x shot 06

Replace the three acpSource boolean tests in agentSessionTranscriptPresentation.test.mjs
(which only asserted item.acpSource === 'raw_json_rpc' on hand-built objects — they would
pass with the isRawPayload branch deleted) with proper renderToStaticMarkup render tests in
RawRailActivity.render.test.mjs. The new tests render RawRailActivity for three metadata
items and assert the HTML: raw_json_rpc includes <pre> and no rounded-2xl; system prompt
and steer-turn prompt context include rounded-2xl and no <pre>.

Fix shot 06 in observer-feed-screenshots.spec.ts: after the render unification,
system-prompt sections are React button accordions inside a native ActivityRow <details>.
Shot 06 now opens the outer <details> first, then clicks each inner section button to
expand it — matching the interaction pattern in shot 05. Shot 06 confirmed to show both
Base and System sections fully expanded in the polished card UI.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Add count assertion before the section-button click loop in shot 06 so a
future testid/nesting break fails loudly instead of silently producing a
collapsed screenshot.

Swap the render-test accordion marker from the style token 'rounded-2xl'
to 'data-testid="transcript-prompt-context-sections"' — the semantic
shared-list marker emitted by PromptSectionList. Style tokens can drift
without breaking behavior; the testid is stable and specific.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96

Copy link
Copy Markdown
Collaborator Author

Observer feed screenshots — PR #1412 (rebuilt)

E2E Playwright spec (observer-feed-screenshots.spec.ts) seeds fixture observer events through the real appendAgentEvent → processTranscriptEvent ingestion path (not a stub), then captures six surfaces restored or improved by this PR. Rebuilt after the render unification so the shots match the current UI.

Prompt context — inline collapsed (Fix 1)

01-prompt-context-inline

Per-turn prompt context sections are visible inline in the feed, collapsed but labeled with section titles — no longer buried in a modal.

Prompt context — sections expanded (Fix 1, expanded)

05-prompt-context-expanded

The same inline block with all three accordion sections clicked open, showing section titles and content in the polished rounded-2xl card.

System prompt title restored (Fix 2)

02-system-prompt-title-restored

The system prompt item shows its real title ("System prompt") and section count instead of the anonymous "Captured N raw sections" label.

System prompt sections expanded (Fix 2 + render unification)

06-system-prompt-expanded

The system prompt item expanded, showing the "Base" and "System" sections. These now render in the same polished section-accordion card as prompt context (above) instead of the raw <pre> monospace treatment — the render unification this PR adds. Raw ACP payloads still keep the <pre> safety net.

Permission outcome — approved (Fix 3)

03-permission-approved

Three-part permission block: request row, muted options sub-line, then a visually distinct decision row with green ✓ "Approved (allow_once)".

Permission outcome — cancelled (Fix 3)

04-permission-cancelled

Same three-part block with a muted grey "Cancelled" decision row when the user dismisses without selecting.

wpfleger96 pushed a commit that referenced this pull request Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant