Skip to content

[#973] Frontend correctness bundle#983

Merged
realproject7 merged 1 commit into
mainfrom
task/973-frontend-correctness
Jul 6, 2026
Merged

[#973] Frontend correctness bundle#983
realproject7 merged 1 commit into
mainfrom
task/973-frontend-correctness

Conversation

@realproject7

@realproject7 realproject7 commented Jul 6, 2026

Copy link
Copy Markdown
Owner

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

  1. 403 auth banner was dead codeauthRetryRef was 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.
  2. Project-switch poll race — an in-flight poll for the old project could resolve after the operator switched and append its messages / advance the cursor into the new project. The fetch now captures its reqProject and drops stale responses (and stale retries), mirroring the cancelled/stale-read guard in ProjectDashboard.tsx:154-166.
  3. (fold) Reply button prepended @sender by replacing the input, discarding any draft; now prepends without discarding. Message + preview attachment keys 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 (new syncSavedProjectName / syncSavedAgentName, modeled on the existing syncSavedIdle); the optimistic update already reflects the new name in local state.
5. Swallowed load error → infinite spinner — a failed initial /api/config read left config null and the "Loading…" placeholder spinning forever. Adds an error + Retry state (pattern from HomeDashboard: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 setTimeout leaked (never stored/cleared); now held in a ref, cleared before re-arm and on unmount.

TerminalPanel.tsx
8. Resize spamfit() sent a resize frame every animation frame during output bursts even when dimensions were unchanged. Now tracks last-sent cols/rows and only sends on an actual change (seeded from the onopen initial 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 finally after use (the server keeps the PTY alive with zero viewers — verified in server/index.js:1726 ws.on("close"), which only removes the viewer). The fixed 500ms spawn sleep is replaced with polling /api/sessions for 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 skipped
  • npm run build (Next.js production) → compiled successfully; /settings and /project/[id]/queue (the changed components' routes) prerender cleanly
  • npx eslint on the 5 files → 0 new errors (only pre-existing <img> warnings + pre-existing SystemSection ref 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)

Aspect Requirement Evidence
Pattern reuse Match sibling destructive buttons (Full Reset / Re-seed) Armed state reuses their identical class string text-error border-error/60 bg-error/10 hover:bg-error/20 (:309-311, same as Full Reset :321)
Colors Design tokens only --error #ff4444 (armed), --accent #00ff88 (idle hover), --border #2a2a2a — no literals added
Typography Match row text-[10px], Geist Mono inherited — same as every button in the Server row
Spacing / layout No layout drift px-1.5 py-0.5; button stays in the existing flex items-center gap-1.5 flex-wrap row — no structural change
Corners / depth Sharp, no shadow/glow no rounded/shadow classes — matches dev-tool aesthetic
States idle → armed → loading → auto-reset idle t.resetAgents → armed t.confirmResetAgents (:314) → "..." while loading==="reset" → 4s auto-clear (:218-224); disabled:opacity-50
i18n en + ko copy :22 / :75 added to existing COPY tables
Responsive inherits row no new breakpoints; row already flex-wrap

2 — SettingsPage load-error + Retry (src/components/SettingsPage.tsx:838-852)

Aspect Requirement Evidence
Pattern reuse Reuse the app's error-banner vocabulary Banner border border-error/30 bg-error/5 text-error text-[11px] px-3 py-2 (:841) mirrors the HomeDashboard:198 load-error banner
Retry control Standard secondary button border border-border text-text-muted hover:text-text hover:border-accent (:846-849) — same as other secondary buttons on the page
Colors Design tokens only --error (banner), --accent (button hover), --border — tokens only
Typography Match page scale banner text-[11px], button text-[12px] — consistent with surrounding Settings text
Spacing / layout Match replaced placeholder container p-6 flex flex-col items-start gap-3 — same p-6 as the Loading… placeholder it supersedes (:854)
States null+error → error/Retry → recovery replaces the infinite spinner; Retry re-invokes load(), which clears loadError (:391) and re-fetches
i18n en + ko copy :77 / :166 added
Dark-mode only no light theme all classes resolve to the dark palette; no light branch introduced

🤖 Generated with Claude Code

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 project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 -> head 000b5d4bc5cbde115b6ba7302fee8cd7b6833fdf; body has ## EPIC Alignment and ## Self-Verification, but ## Design Fidelity is 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 Fidelity table.
    • 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.

Decision

REQUEST CHANGES. I did not review the diff; resubmit with the required Design Fidelity table and I will run the full live review.

@realproject7

Copy link
Copy Markdown
Owner Author

APPROVE at 000b5d4bc5cbde115b6ba7302fee8cd7b6833fdf

Full independent review — issue #973 Frontend correctness bundle (EPIC #967 Phase 3). Frontend-only; diff touches only src/components/* (ChatPanel, ControlBar, QueueManager, SettingsPage, TerminalPanel), zero server changes. Verified each fix against the actual contracts it depends on:

  1. ChatPanel 403 counterauthRetryRef now increments on 403 (was dead code), resets on clean poll and on project switch. Banner branch is now reachable.
  2. ChatPanel project-switch racereqProject captured in the fetchMessages closure (deps [channel, projectId, loaded], so it re-binds per project), compared against projectRef.current (set during render) in both .then and .catch; stale responses/retries dropped. Mirrors the ProjectDashboard guard.
  3. SettingsPage rename clobber.then(load()) replaced with syncSavedProjectName/syncSavedAgentName, which advance only the renamed field in savedConfigRef. Fields match the optimistic updates (name / display_name); unsaved edits in other fields survive the 800ms debounce.
  4. SettingsPage load error.catch(() => setLoadError(true)) + if (!config && loadError) error/Retry view; load() clears loadError on entry. No more infinite spinner.
  5. ControlBar Reset Agents — two-click confirm with 4s auto-clear, matching Full Reset / Re-seed.
  6. TerminalPanel resizelastSentSizeRef gate sends only on actual cols/rows change; seeded in onopen so reconnects still send the initial resize.
  7. QueueManager WS leak — bootstrap WS closed in finally; readiness now polls /api/sessions (confirmed server returns {projectId, agentId} for state==="running" sessions) instead of a fixed 500ms sleep. CLI warm-up stays a fixed 3s (no liveness signal), documented.

Folds: reply-draft now prepends the mention (preserves draft); attachment keys composite name-i (both render sites); ControlBar feedback timer ref'd + cleared on re-arm and unmount.

Verification: tsc --noEmit clean locally (shared deps), CI test SUCCESS. No findings.

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 -> head 000b5d4bc5cbde115b6ba7302fee8cd7b6833fdf; 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 at src/components/ChatPanel.tsx:238, rename snapshot merge at src/components/SettingsPage.tsx:762, load error/retry at src/components/SettingsPage.tsx:838, reset confirm at src/components/ControlBar.tsx:245, resize gate at src/components/TerminalPanel.tsx:56, bootstrap WS close at src/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/sessions is 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 -> test pass, 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.

@realproject7 realproject7 merged commit d7cf39f into main Jul 6, 2026
1 check passed
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.

[#S7] Frontend correctness bundle (chat auth banner, project-switch race, rename-clobber, confirms)

2 participants