Mark the Document tab when the Planner writes out of view #372
- MA
## Why
After answering a decision in Decisions, the Planner's resumed turn rewrote the document while the reader stayed on Decisions. The Document tab gave no sign of it, and by the time they looked, the change washes had partly faded (real-Planner audit, finding 5). The same happened on a phone while the reader was in Chat.
## What changed
- The Document tab (the desktop view control and the phone tab bar) shows a small petrol mark while the document is out of view: - **Writing:** a hollow ring that breathes, using the existing `chat-work-dot` keyframes and motion tokens. It shows while a Planner turn runs and either agent changes landed during that turn, or this viewer answered a decision out of view and the turn resumed. The pulse runs only under `prefers-reduced-motion: no-preference`; otherwise the ring is still. - **New changes:** a filled dot. It shows once agent changes are waiting and the turn has ended. - The mark clears as soon as the document is viewed, like the Chat unread badge. - On desktop the mark sits in the button's corner padding, so the Decisions pill never moves. On a phone it sits inline in the wide tab cell. - An answer is followed only when it is this viewer's own (`question:resolved` with `status: "answered"` and `resolver === handle`). It is forgotten if no turn starts within 3 seconds. Discards, other people's answers and unrelated turns never read as "Planner writing". - **Only agent edits mark the tab.** The server sends `plan:changes` only for Planner and MCP (coding-agent) edits, never for collaborators typing. - The accessible names are `Document, Planner writing` and `Document, new changes`. - The logic is a small pure reducer in `document-activity.tsx`. - Not changed: the open card's **Discard** button. It discards the decision itself, behind a confirmation step, not a draft. So it isn't true that there's nothing to discard, and disabling it would remove the only way to dismiss an unanswered decision. I also left out the optional inline line under the answered card, because `decisions.tsx` is under #347 and #302.
## Screenshots
These show Decisions open with changes landing out of view.
Before (desktop): no cue 
After (desktop): new changes 
After (desktop): Planner writing (the dot breathes) 
After (desktop): the cue clears once the Document is viewed 
Before (phone) 
After (phone) 
After the review fixes:
Desktop, Planner writing: a ring in the corner, so nothing shifts 
Desktop, new changes: a filled dot 
Phone, new changes 
## Verification
Nine focused document-activity tests, `bun run types`, `bun run ci`, and `bun run build` pass on the restacked branch. GitHub's browser suite will run the desktop and phone activity scenarios on the final published head. The reviewed workspace design hash was renewed after checking the integrated source.
- MA
**Review:** Changes needed
Checked in a real browser (own headless Playwright, fake Planner, desktop 1440x900 and phone 390x844) and in code. The dot itself is calm and matches the Chat unread dot (size-1.5, `bg-brand`); names are `Document, Planner writing` / `Document, new changes`; it clears on view. Two real defects and two smaller points.
1. **Reduced motion is not honoured (bug).** `theme.css` `.document-activity-dot { animation: none }` inside the media query has specificity (0,1,0) and loses to `.document-activity-dot[data-document-activity="writing"]` (0,2,0). With `reducedMotion: "reduce"` emulated, the computed `animation-name` is still `chat-work-dot` and opacity oscillates (measured 0.30). Fix: use `.document-activity-dot[data-document-activity="writing"]` (or `:is`) in the media-query selector, or put the animation inside `@media (prefers-reduced-motion: no-preference)`. Also consider: once static, "writing" and "new changes" look identical (only the accessible name differs); a static ring or lower opacity for writing would keep them distinct.
2. **False positive: `following` goes stale (`room-workspace.tsx` answered effect / `document-activity.tsx`).** `answered` fires on any decrease of `unanswered`, including Discard, another collaborator answering, and answers that start no Planner turn (the fake Planner did not start one for a seeded decision; Chat stayed empty). `following` is only cleared on a busy-to-idle edge, so it stays set. Repro: on Decisions, Discard a decision, then send an unrelated `@chopin ...` from Chat: the Document tab reads "Document, Planner writing" for the whole turn although nothing will be written. Same after answering with no resumed turn. Suggested fix: only set `following` for the viewer's own submitted answer (not discard or remote answers), and expire it if Chat does not become busy within a short window (or clear it when a turn starts that is not the answer's), so unrelated turns cannot inherit it.
3. **Collaborators' edits (decision).** `plan:changes` is only broadcast for Planner/MCP edits, so human collaborator typing never marks the tab; a coding agent or MCP update (even from another user) does. I think that is the least surprising rule (the cue means "the Planner/agent changed the document"). Worth stating in the PR/docs; the tab name "new changes" could mislead if human edits were ever included.
4. **Minor: layout shift.** The dot is inline (`ml-1`), so the Decisions pill moves about 18px when it appears/disappears (desktop control). The Chat dot is absolutely positioned and does not shift. Reserve the space or position the dot absolutely. On phone the inline dot is fine but shares the bar with the Chat spinner; acceptable.
Code/tests: reducer is clean and tested; e2e covers unseen and cleared on both viewports, but nothing covers Discard/unrelated-turn or reduced motion; add a unit case for the stale `following` once fixed. workspace.tsx hash is the known design-contract item.
- MA
Thanks for the review. All four points are addressed in 4458b801:
1. **Reduced motion.** The pulse now lives inside `@media (prefers-reduced-motion: no-preference)`. With `reducedMotion: "reduce"` emulated, the computed `animation-name` is `none` and opacity is 1. "Writing" is now a hollow ring (`size-2 border-2 border-brand`) and "new changes" is a filled dot, so they stay distinct when still. 2. **Stale `following`.** It is set only from `question:resolved` with `status: "answered"` and `resolver === handle`, so this viewer's own answer counts and discards (`cancelled`) and other people's answers don't. It waits as `awaiting`, becomes active when Chat goes busy, or expires after 3s. Agent changes during a running turn also mark it active, but older unseen changes no longer make a new turn read as writing. Checked in a browser: a Discard followed by an unrelated `@chopin SLOW-LIVE` turn leaves the tab as plain "Document". 3. **Collaborator edits.** The PR now states that only Planner/MCP edits mark the tab, because `plan:changes` is never sent for collaborators typing. The same note is in the module doc comment. 4. **Layout shift.** On desktop the mark is absolutely positioned in the button's corner padding, and the Decisions pill moves 0px when it appears or clears. The phone tab bar keeps it inline.
Tests: - New unit cases cover a discard followed by an unrelated turn, an expired answer, old unseen changes plus a new turn, and the reduced-motion rule. - A new e2e checks that a discard out of view leaves the tab quiet. - `bun test apps/web` gives 511 pass, and types pass. - CI still fails only on the known `workspace.tsx` design-contract hash (`1a974e3b…`).
- MA
Follow-up 84fb42dc: removed the discard E2E case. The main E2E server runs with `DEV_QUESTIONS` off, so it has no decision to discard. Unit tests in `document-activity.test.ts` already cover a discard followed by an unrelated turn. CI: e2e and container pass; format/lint/types/tests fails only on the known `workspace.tsx` design-contract hash.
MaggieAppletonhead-ref-force-pushed 19a9f7ec2e24last month on GitHub
MaggieAppletonreferenced this in Pilot guarded PR readiness repairs #399last month on GitHub
github-actions[bot]added label maintenance:workinglast month on GitHub
<!-- chopin:pr-maintenance:v1 --> **PR maintenance: needs attention**
Proposal artifact or Git policy validation failed
Needed decision: resolve the blocker above or request another maintenance attempt.
Owner: @MaggieAppleton
Inspected head: 8b6af664574b7547f0a5f1d14c1a386a7eca15e2
Base head: 35549a43499f2a3d1cb7f957212b415475d34631
[PR readiness runs](https://github.com/githubnext/chopin/actions/workflows/pr-readiness.yml) <!-- chopin:pr-maintenance-auth:eyJwYXlsb2FkIjp7InNjaGVtYVZlcnNpb24iOjEsInJlcG9zaXRvcnkiOiJnaXRodWJuZXh0L2Nob3BpbiIsInJldmlzaW9uIjowLCJwcnMiOnsiMzcyIjp7ImJvZHkiOiI8IS0tIGNob3Bpbjpwci1tYWludGVuYW5jZTp2MSAtLT5cbioqUFIgbWFpbnRlbmFuY2U6IG5lZWRzIGF0dGVudGlvbioqXG5cblByb3Bvc2FsIGFydGlmYWN0IG9yIEdpdCBwb2xpY3kgdmFsaWRhdGlvbiBmYWlsZWRcblxuTmVlZGVkIGRlY2lzaW9uOiByZXNvbHZlIHRoZSBibG9ja2VyIGFib3ZlIG9yIHJlcXVlc3QgYW5vdGhlciBtYWludGVuYW5jZSBhdHRlbXB0LlxuXG5Pd25lcjogQE1hZ2dpZUFwcGxldG9uXG5cbkluc3BlY3RlZCBoZWFkOiA4YjZhZjY2NDU3NGI3NTQ3ZjBhNWYxZDE0YzFhMzg2YTdlY2ExNWUyXG5cbkJhc2UgaGVhZDogMzU1NDlhNDM0OTlmMmEzZDFjYjdmOTU3MjEyYjQxNTQ3NWQzNDYzMVxuXG5bUFIgcmVhZGluZXNzIHJ1bnNdKGh0dHBzOi8vZ2l0aHViLmNvbS9naXRodWJuZXh0L2Nob3Bpbi9hY3Rpb25zL3dvcmtmbG93cy9wci1yZWFkaW5lc3MueW1sKSIsImJsb2NrZXJJZCI6IjQyZjg0MGQ5LTlhY2ItNGM1OS04Zjg4LTUxN2YzZTlmZjNlOSJ9fX0sInNpZ25hdHVyZSI6IjY0YTA4MDJhYTZkNDRjZWRjOWE2M2NjOTUzZjg0MDFiMDc1MzQzMTNlYzk0Y2I2NzZlMjAyMGNkZGVmNWUyNjgifQ== -->
MaggieAppletonreferenced this in Fix PR readiness pilot review and worker wakeups #400last month on GitHub
MaggieAppletonreferenced this in Recover PR readiness worker dispatch #401last month on GitHub
MaggieAppletonreferenced this in Clarify source paths in PR readiness hash reviews #403last month on GitHub
github-actions[bot]removed label maintenance:workinglast month on GitHub
github-actions[bot]added label needs-human-attentionlast month on GitHub
MaggieAppletonmentionedlast month on GitHub
MaggieAppletonsubscribedlast month on GitHub
MaggieAppletonhead-ref-force-pushed 2f8c2838d005last month on GitHub
MaggieAppletonhead-ref-force-pushed 01d419377a50last month on GitHub
MaggieAppletonmerged 9976c6355430last month on GitHub
MaggieAppletonhead-ref-deletedlast month on GitHub
MaggieAppletonreferenced this in Run PR readiness on open non-draft PRs and provision test databases #424last month on GitHub