[#973] Frontend correctness bundle#983
Conversation
Fixes 7 confirmed frontend-only bugs + 3 folded lows across the dashboard components. No server changes. ChatPanel: - 403 auth banner was dead code (authRetryRef read, never incremented); now counts failures and clears the tally on a clean poll / project switch. - project-switch poll race: an in-flight poll for the prior project could resolve after switching and pollute the new list + cursor. Captures the request's project and drops stale responses (mirrors ProjectDashboard). - fold: reply button no longer discards the typed draft; attachment keys are index-composited to avoid same-filename collisions. SettingsPage: - debounced rename called load(), clobbering unsaved edits made during the 800ms window; now merges only the renamed field into the saved snapshot. - failed initial /api/config read left the "Loading..." spinner forever; adds an error + Retry state. ControlBar: - Reset Agents was a single-click kill of all 4 agents; adds the two-click confirm the other destructive buttons use. - fold: feedback-clear timer no longer leaks (cleared before re-arm + on unmount). TerminalPanel: - resize was sent per animation frame during output bursts; only sends when cols/rows actually change. QueueManager: - bootstrap WebSocket was never closed (leak); closes it after use and polls /api/sessions for liveness instead of a fixed spawn sleep. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Epic Alignment: NOT REVIEWED
Structural gate failed before code review; PR body must include the required UI Design Fidelity table before RE1 can assess EPIC #967 alignment.
Checked (evidence)
- Structural gate:
gh pr view 983 --json body,headRefOid-> head000b5d4bc5cbde115b6ba7302fee8cd7b6833fdf; body has## EPIC Alignmentand## Self-Verification, but## Design Fidelityis prose bullets rather than the required table for a UI PR. - Riskiest part of this diff: NOT REVIEWED because the required PR template gate failed.
- Kill-list: NOT REVIEWED because the required PR template gate failed.
- CI: NOT REVIEWED because the required PR template gate failed.
Findings
- [blocking] UI PR is missing the required
## Design Fidelitytable.- File: PR body (
## Design Fidelity) - Why it fails: This frontend PR changes visible UI behavior, but the section is two prose bullets. RE1 rules require a Design Fidelity table for UI PRs before code review starts.
- Do instead: Replace the prose bullets with a table that maps each visible/spec design requirement to the implemented file:line evidence, including layout/spacing/typography/colors/states/responsive coverage where applicable.
- File: PR body (
Decision
REQUEST CHANGES. I did not review the diff; resubmit with the required Design Fidelity table and I will run the full live review.
|
APPROVE at Full independent review — issue #973 Frontend correctness bundle (EPIC #967 Phase 3). Frontend-only; diff touches only
Folds: reply-draft now prepends the mention (preserves draft); attachment keys composite Verification: |
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Epic Alignment: PASS
EPIC #967 Phase 3 calls for isolated frontend correctness fixes without server changes; PR #983 only changes src/components/* and covers #973's listed frontend bugs.
Checked (evidence)
- Structural gate:
gh pr view 983 --json body,headRefOid-> head000b5d4bc5cbde115b6ba7302fee8cd7b6833fdf; required sections present, including Design Fidelity tables. - Context: issue #973 goal/scope and epic #967 Goal/Architecture/Contracts read live via
gh issue view 973+gh api repos/realproject7/quadwork/issues/967. - Frontend-only scope:
gh pr diff 983 --name-only->src/components/ChatPanel.tsx,ControlBar.tsx,QueueManager.tsx,SettingsPage.tsx,TerminalPanel.tsx; no server endpoints changed. - Acceptance coverage: 403 counter/banner at
src/components/ChatPanel.tsx:224, stale-project drop atsrc/components/ChatPanel.tsx:238, rename snapshot merge atsrc/components/SettingsPage.tsx:762, load error/retry atsrc/components/SettingsPage.tsx:838, reset confirm atsrc/components/ControlBar.tsx:245, resize gate atsrc/components/TerminalPanel.tsx:56, bootstrap WS close atsrc/components/QueueManager.tsx:200. - Design Fidelity: spot-checked table rows against
src/components/ControlBar.tsx:308,src/components/ControlBar.tsx:314,src/components/SettingsPage.tsx:840,src/components/SettingsPage.tsx:846, and COPY/i18n rows; table matches code. - Riskiest part of this diff: QueueManager closes the bootstrap WS after spawning Head; acceptable because server
/api/sessionsis an existing liveness probe and the PTY is retained independently of viewers. - Kill-list: scanned all items — clean; raw-value scan found no added TODO/FIXME/HACK, console/debugger, raw hex, or off-scale arbitrary pixel classes in changed lines.
- CI:
gh pr checks 983->testpass, 51s.
Findings
None.
Decision
APPROVE. The implementation is local, matches the ticket and epic constraints, and the visible UI changes reuse existing patterns without introducing new visual vocabulary.
Closes #973
Frontend correctness bundle — 7 confirmed frontend-only bugs + 3 folded lows across the dashboard components. No server endpoints touched; existing patterns matched throughout.
Changes
ChatPanel.tsx
authRetryRefwas read (< 3) but never incremented, so the "Chat authentication failed (403)" banner could never surface. Now increments on each 403, shows once the retry budget is spent, and resets the tally on a clean poll and on project switch.reqProjectand drops stale responses (and stale retries), mirroring thecancelled/stale-read guard inProjectDashboard.tsx:154-166.@senderby replacing the input, discarding any draft; now prepends without discarding. Message + preview attachmentkeys are index-composited so two attachments sharing a filename don't collide.SettingsPage.tsx
4. Debounced rename clobbered unsaved edits — the 800ms rename timer called
load(), overwriting any edits made to other fields during the window. Now merges only the renamed field into the saved snapshot (newsyncSavedProjectName/syncSavedAgentName, modeled on the existingsyncSavedIdle); the optimistic update already reflects the new name in local state.5. Swallowed load error → infinite spinner — a failed initial
/api/configread leftconfignull and the "Loading…" placeholder spinning forever. Adds an error + Retry state (pattern fromHomeDashboard:116-138).ControlBar.tsx
6. Reset Agents had no confirm — a single click killed all 4 agents, unlike every other destructive button here. Adds the same two-click confirm + 4s auto-clear used by Full Reset / Re-seed.
7. (fold) Feedback-clear
setTimeoutleaked (never stored/cleared); now held in a ref, cleared before re-arm and on unmount.TerminalPanel.tsx
8. Resize spam —
fit()sent a resize frame every animation frame during output bursts even when dimensions were unchanged. Now tracks last-sentcols/rowsand only sends on an actual change (seeded from theonopeninitial resize).QueueManager.tsx
9. Bootstrap WebSocket leak — the WS opened to spawn the Head PTY was never closed, leaking for the page lifetime. Now closed in a
finallyafter use (the server keeps the PTY alive with zero viewers — verified inserver/index.js:1726ws.on("close"), which only removes the viewer). The fixed 500ms spawn sleep is replaced with polling/api/sessionsfor liveness; the CLI warm-up stays a short fixed delay since no liveness signal exists for CLI readiness.EPIC Alignment (#967)
EPIC #967 Phase 3 — isolated frontend correctness pass, no server change (Phases 1–2 landed the atomic-config / field-scoped write backend). This PR only touches
src/components/*, keeping the display-layer/logic-layer split the project enforces.Self-Verification
npx tsc --noEmit→ clean (exit 0)npm test(server suite, what CI runs) → 59 passed, 0 failed, 2 skippednpm run build(Next.js production) → compiled successfully;/settingsand/project/[id]/queue(the changed components' routes) prerender cleanlynpx eslinton the 5 files → 0 new errors (only pre-existing<img>warnings + pre-existingSystemSectionref warnings I did not touch)Design Fidelity
5 of the 7 bugs are logic-only (no rendered output): ChatPanel 403-count + poll-race guard, SettingsPage rename merge, TerminalPanel resize gate, QueueManager WS lifecycle, ControlBar feedback-timer, ChatPanel attachment
keys. Only two changes render new UI; both reuse existing components' token vocabulary with no new visual primitives.1 — ControlBar "Reset Agents" destructive-confirm (
src/components/ControlBar.tsx:305-316)text-error border-error/60 bg-error/10 hover:bg-error/20(:309-311, same as Full Reset:321)--error#ff4444(armed),--accent#00ff88(idle hover),--border#2a2a2a— no literals addedtext-[10px], Geist Mono inherited — same as every button in the Server rowpx-1.5 py-0.5; button stays in the existingflex items-center gap-1.5 flex-wraprow — no structural changerounded/shadowclasses — matches dev-tool aesthetict.resetAgents→ armedt.confirmResetAgents(:314) →"..."whileloading==="reset"→ 4s auto-clear (:218-224);disabled:opacity-50:22/:75added to existing COPY tablesflex-wrap2 — SettingsPage load-error + Retry (
src/components/SettingsPage.tsx:838-852)border border-error/30 bg-error/5 text-error text-[11px] px-3 py-2(:841) mirrors theHomeDashboard:198load-error bannerborder border-border text-text-muted hover:text-text hover:border-accent(:846-849) — same as other secondary buttons on the page--error(banner),--accent(button hover),--border— tokens onlytext-[11px], buttontext-[12px]— consistent with surrounding Settings textp-6 flex flex-col items-start gap-3— samep-6as theLoading…placeholder it supersedes (:854)load(), which clearsloadError(:391) and re-fetches:77/:166added🤖 Generated with Claude Code