diff --git a/.ai/HANDOFF.md b/.ai/HANDOFF.md index 242d8a38..bfca176f 100644 --- a/.ai/HANDOFF.md +++ b/.ai/HANDOFF.md @@ -2,23 +2,24 @@ # HANDOFF.md -**Last updated:** 2026-05-01 (session 8 — cleaned stale TODOs, wrote issue cleanup plan) +**Last updated:** 2026-05-01 (session 9 — started issue cleanup plan sections 1 and 2) **Active task:** None. Pick next from `.ai/TODO.md` or roadmap. -**Just-updated:** stale local TODOs were removed and an issue cleanup plan was added. +**Just-updated:** issue cleanup plan sections 1 and 2 were started and documented. ## Where this session ended -Cleanup follow-up completed: +Issue cleanup plan follow-up completed: -- `.ai/TODO.md`: removed resolved pytest-xdist "Up next" item, removed resolved claim-role-gate item, updated frontend lint count to 24 warnings. -- Added `docs/plans/2026-05-01-issue-cleanup-plan.md` with tracker hygiene and implementation order. -- Tried to close Gitea #127 via API, but this environment has no Gitea token; API returned `401 token is required`. +- Section 1: frontend lint is clean. Stale lint disables from the warning set were removed or replaced with justified comments, hook dependency warnings were resolved, e2e selectors were added for session history and the FlowPilot command-palette entry, and `AssistantChatPage` now logs unexpected `currentChatRef` stale async discards. +- Section 2: `TaskLane` action cards now have diagnostic help affordances for common commands (connectivity, DNS, IP config, event logs, services, and generic checks). #128 was documented as "keep existing responsive side-panel/bottom-drawer behavior unless pilot feedback proves a preference is needed." +- Updated `docs/plans/2026-05-01-issue-cleanup-plan.md` with section 1/2 status and validation. +- Validation passed: `docker exec -w /app resolutionflow_frontend npm run lint`, `docker exec -w /app resolutionflow_frontend npx tsc -b`, and `docker exec -w /app resolutionflow_frontend npm run build` (existing Vite large-chunk warning only). ## Resume point — DO THIS NEXT -If tracker auth is available, close #127 and close/archive stale PR #124; rewrite #66 to template packs / one-click install only. Then pick from the plan: low-risk maintenance first, then #130/#128 pilot UX friction. +If tracker auth is available, close #127 and close/archive stale PR #124; rewrite #66 to template packs / one-click install only. Then continue the plan at section 3: #58 structured "step is wrong" quality signals. After that, section 4 is #60 recurring issue detection and section 5 is #129 hierarchical guide navigation. ## Environment notes (carry-forward) diff --git a/.ai/SESSION_LOG.md b/.ai/SESSION_LOG.md index 506cb40f..57a41ebd 100644 --- a/.ai/SESSION_LOG.md +++ b/.ai/SESSION_LOG.md @@ -12,6 +12,16 @@ --- +## 2026-05-01 07:20 UTC — Codex — Start issue cleanup plan sections 1 and 2 + +- Started `docs/plans/2026-05-01-issue-cleanup-plan.md` sections 1 and 2. +- Cleaned frontend lint to zero warnings by removing stale lint disables, tightening hook dependencies, and adding justified comments where effects are intentionally keyed to route or owner identity. +- Added e2e selectors for session history controls and the FlowPilot command-palette entry. +- Added `AssistantChatPage` observability for unexpected `currentChatRef` stale async discards. +- Added `TaskLane` diagnostic help affordances for common command categories and documented #128 as "keep the existing responsive side-panel/bottom-drawer behavior until pilot feedback says otherwise." +- Verified `npm run lint`, `npx tsc -b`, and `npm run build` in `resolutionflow_frontend`; build only reported the existing Vite large-chunk warning. +- Files touched: frontend lint-cleanup files, `frontend/src/components/assistant/TaskLane.tsx`, `frontend/src/pages/AssistantChatPage.tsx`, `frontend/src/pages/SessionHistoryPage.tsx`, `frontend/src/components/layout/CommandPalette.tsx`, `docs/plans/2026-05-01-issue-cleanup-plan.md`, `.ai/HANDOFF.md`, `.ai/SESSION_LOG.md`. + ## 2026-05-01 06:05 UTC — Codex — Clean stale TODOs and add issue cleanup plan - Removed the resolved pytest-xdist item from `.ai/TODO.md` and reset "Up next" to no selected task. diff --git a/docs/plans/2026-05-01-issue-cleanup-plan.md b/docs/plans/2026-05-01-issue-cleanup-plan.md index c0378a0b..25fbf983 100644 --- a/docs/plans/2026-05-01-issue-cleanup-plan.md +++ b/docs/plans/2026-05-01-issue-cleanup-plan.md @@ -19,21 +19,28 @@ These are safe tracker updates before any feature work: ### 1. Low-Risk Maintenance -- Clean frontend lint warnings. -- Audit/remove stale eslint-disable comments. -- Add missing `data-testid` selectors for e2e-critical controls. -- Add observability around unexpected `currentChatRef` guard mismatches. +- Status: started 2026-05-01. +- Frontend lint is clean after removing stale disable comments and tightening hook dependencies. +- Added `data-testid` selectors for e2e-critical session history and FlowPilot command-palette controls. +- Added `AssistantChatPage` observability for unexpected `currentChatRef` guard mismatches so stale async discards are visible in the console. Why first: these reduce future regression cost and are small, well-bounded changes. ### 2. Pilot UX Friction -- #130: Add diagnostic command help affordances in `TaskLane` / action cards. -- #128: Decide whether task panel placement should be configurable or whether the existing responsive drawer is enough. +- Status: started 2026-05-01. +- #130: Added diagnostic command help affordances in `TaskLane` action cards. Each active diagnostic card can explain what it checks, what to look for, and when to use it. +- #128: Keep the existing responsive drawer behavior for now. `TaskLane` already uses a side panel on wide screens and a bottom drawer below the desktop breakpoint; do not add a top/side preference unless pilot feedback shows the current responsive layout is blocking workflow. - EscalationQueue mobile design stays deferred until a customer asks for it. Why second: this improves the current FlowPilot wedge without changing core data models. +Validation run: + +- `docker exec -w /app resolutionflow_frontend npm run lint` +- `docker exec -w /app resolutionflow_frontend npx tsc -b` +- `docker exec -w /app resolutionflow_frontend npm run build` + ### 3. Workflow Quality Signals - #58: Add structured "step is wrong" flags separate from thumbs-up/down helpfulness. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c3a27f1c..1487ac33 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,7 +16,7 @@ function App() { } else { setLoading(false) } - }, []) + }, [fetchUser, isAuthenticated, setLoading]) return } diff --git a/frontend/src/components/analytics/FlowAnalyticsPanel.tsx b/frontend/src/components/analytics/FlowAnalyticsPanel.tsx index 19923775..0c0a736c 100644 --- a/frontend/src/components/analytics/FlowAnalyticsPanel.tsx +++ b/frontend/src/components/analytics/FlowAnalyticsPanel.tsx @@ -39,7 +39,7 @@ export function FlowAnalyticsPanel({ treeId }: FlowAnalyticsPanelProps) { useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect setLoading(true) - // eslint-disable-next-line react-hooks/set-state-in-effect + setError(false) analyticsApi .getFlowAnalytics(treeId, period) diff --git a/frontend/src/components/assistant/TaskLane.tsx b/frontend/src/components/assistant/TaskLane.tsx index b0ecf5b4..4311cff4 100644 --- a/frontend/src/components/assistant/TaskLane.tsx +++ b/frontend/src/components/assistant/TaskLane.tsx @@ -31,6 +31,62 @@ interface ActionResponse { type TaskResponse = QuestionResponse | ActionResponse +interface DiagnosticHelp { + what: string + lookFor: string + usefulWhen: string +} + +function getDiagnosticHelp(action: ActionResponse): DiagnosticHelp { + const command = (action.command || '').toLowerCase() + + if (command.includes('test-netconnection') || command.includes('ping ')) { + return { + what: action.description || 'Checks whether the target is reachable over the network.', + lookFor: 'Successful replies, low packet loss, and whether the expected port shows as open.', + usefulWhen: 'Use it when you need to separate a service problem from a basic connectivity problem.', + } + } + + if (command.includes('nslookup') || command.includes('resolve-dnsname')) { + return { + what: action.description || 'Checks how DNS resolves the hostname or record.', + lookFor: 'Wrong IPs, NXDOMAIN responses, timeout errors, or different answers from different resolvers.', + usefulWhen: 'Use it when names fail but direct IP access may still work.', + } + } + + if (command.includes('ipconfig') || command.includes('get-netipconfiguration')) { + return { + what: action.description || 'Shows local IP, gateway, DNS, and adapter configuration.', + lookFor: 'APIPA addresses, missing gateways, wrong DNS servers, disconnected adapters, or stale leases.', + usefulWhen: 'Use it early when the symptom may be local network configuration.', + } + } + + if (command.includes('get-eventlog') || command.includes('get-winevent') || command.includes('eventlog')) { + return { + what: action.description || 'Reads Windows event logs for recent errors or warnings.', + lookFor: 'Events matching the failure time, repeated error IDs, service crashes, or permission failures.', + usefulWhen: 'Use it when the UI only shows a generic error and you need system-level evidence.', + } + } + + if (command.includes('get-service') || command.includes('restart-service')) { + return { + what: action.description || 'Checks service state on the affected machine.', + lookFor: 'Stopped services, restart loops, disabled startup types, or dependency failures.', + usefulWhen: 'Use it when a feature depends on a Windows service or background agent.', + } + } + + return { + what: action.description || 'Runs the diagnostic check suggested by FlowPilot.', + lookFor: 'Errors, unexpected values, failed checks, or output that differs from a known-good machine.', + usefulWhen: 'Use it when you need evidence before choosing the next troubleshooting step.', + } +} + interface TaskLaneProps { questions: QuestionItem[] actions: ActionItem[] @@ -98,6 +154,7 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa const [showRunAll, setShowRunAll] = useState(false) const [showPreview, setShowPreview] = useState(false) const [copiedKey, setCopiedKey] = useState(null) + const [expandedHelpKey, setExpandedHelpKey] = useState(null) // ── Resize state ── const DEFAULT_WIDTH = 340 @@ -166,22 +223,22 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa questions: questionsRef.current.map(q => ({ text: q.text, context: q.context })), actions: actionsRef.current.map(a => ({ label: a.label, command: a.command, description: a.description })), responses: tasksRef.current as unknown as Array>, - }).catch(() => { /* silent — best-effort save */ }) + }).catch(() => { /* silent - best-effort save */ }) }, 2000) return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current) } - }, [sessionId, tasks]) // eslint-disable-line react-hooks/exhaustive-deps + }, [sessionId, tasks]) // Reset when new tasks come in from AI response — but preserve saved state useEffect(() => { if (sessionId) { const saved = loadTaskState(sessionId) if (saved && saved.length > 0) { - // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: syncs derived state from prop changes + // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: syncs task UI from persisted session state setTasks(saved) return } } - // eslint-disable-next-line react-hooks/set-state-in-effect -- intentional: syncs derived state from prop changes + setTasks([ ...questions.map((q): QuestionResponse => ({ type: 'question', text: q.text, context: q.context, state: 'pending', value: '', @@ -190,7 +247,7 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa type: 'action', label: a.label, command: a.command, description: a.description, state: 'pending', value: '', })), ]) - }, [questions, actions]) // eslint-disable-line react-hooks/exhaustive-deps + }, [questions, actions, sessionId]) const updateTask = (idx: number, updates: Partial) => { setTasks(prev => prev.map((t, i) => i === idx ? { ...t, ...updates } as TaskResponse : t)) @@ -490,10 +547,49 @@ export function TaskLane({ questions, actions, sessionId, onSubmit, onClose, loa return (
-
{a.label}
- {a.description && ( -
{a.description}
- )} +
+
+
{a.label}
+ {a.description && ( +
{a.description}
+ )} +
+ +
+ + {expandedHelpKey === `${idx}` && (() => { + const help = getDiagnosticHelp(a) + return ( +
+
+

+ What it checks: + {help.what} +

+

+ What to look for: + {help.lookFor} +

+

+ When to use it: + {help.usefulWhen} +

+
+
+ ) + })()} {a.command && (
diff --git a/frontend/src/components/layout/CommandPalette.tsx b/frontend/src/components/layout/CommandPalette.tsx index d8c1d225..7bac283f 100644 --- a/frontend/src/components/layout/CommandPalette.tsx +++ b/frontend/src/components/layout/CommandPalette.tsx @@ -296,7 +296,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) { } return result - }, [query, searchFlows, searchSessions, searchAISessions, user]) + }, [query, searchFlows, searchSessions, searchAISessions, user, onPilotSession]) // Flatten all items for keyboard navigation const flatItems: PaletteItem[] = builtGroups.flatMap(g => g.items) @@ -401,6 +401,7 @@ export function CommandPalette({ open, onClose }: CommandPaletteProps) { return (