Skip to content

Video Upload Re-use: Skip re-upload for consecutive runs (Issue #366)#367

Merged
rogermt merged 9 commits into
mainfrom
v0.16.4
Mar 24, 2026
Merged

Video Upload Re-use: Skip re-upload for consecutive runs (Issue #366)#367
rogermt merged 9 commits into
mainfrom
v0.16.4

Conversation

@rogermt
Copy link
Copy Markdown
Owner

@rogermt rogermt commented Mar 23, 2026

Problem

After uploading a video and running a job, pressing "Run Job" again tried to re-upload the same video file instead of re-using the already uploaded video path. This caused:

  • Wasted bandwidth
  • Slower user experience
  • Potential "Network error during upload" on consecutive uploads

Solution

Store the uploaded video_path in local state and re-use it for consecutive job runs. Only upload when user selects a new file.

Changes

VideoUpload.tsx:

  • Added videoPath state to store uploaded video path
  • Modified handleRunJob and handleStartStreaming to check videoPath first
  • Skip upload if videoPath already set
  • Clear videoPath when different file selected
  • Show "Video uploaded ✓" status indicator

VideoUpload.test.tsx (NEW):

  • Test: upload happens on first Run Job click
  • Test: upload is SKIPPED on second Run Job click (re-use videoPath)
  • Test: selecting different file clears videoPath and triggers new upload
  • Test: "Video uploaded" status is shown after successful upload

State Flow

Select File → file set, videoPath=null
Run Job #1 → Upload → videoPath="video/input/xxx.mp4"
Run Job #2 → Skip upload, use videoPath ✓
Select New File → Clear videoPath
Run Job #3 → Upload new file

TEST-CHANGE JUSTIFICATION

Tests added to verify the new videoPath re-use behavior:

  • Ensures upload is skipped on consecutive runs
  • Ensures videoPath is cleared when different file selected
  • Ensures "Video uploaded" status is displayed

Fixes #366

Summary by CodeRabbit

  • Documentation

    • Added a frontend state flow document mapping UI state, data loads and polling interactions.
  • Bug Fixes

    • Prevented stale job status from showing when starting a new video run.
    • Avoided unnecessary status polling for jobs already in terminal states.
    • Ensured job list refreshes when switching views so retries occur as expected.
  • Tests

    • Added tests for view-mode-driven job list fetching, initial-status polling behaviour and consecutive video-run flows.

Roger MT added 4 commits March 23, 2026 03:51
- Add initialStatus prop to JobStatus component
- Initialize pollStatus from initialStatus instead of hardcoded "pending"
- Pass initialStatus from App.tsx for selectedJob and uploadResult
- Add tests for initialStatus prop behavior

This prevents unnecessary polling when a completed job is selected
from the Jobs view. Previously, JobStatus always initialized pollStatus
to "pending" on mount, causing polling to start even for terminal states.

Fixes #363
- Add viewMode prop to JobList component
- Only fetch jobs when viewMode === 'jobs'
- Re-fetch when viewMode changes to 'jobs'
- Pass viewMode from App.tsx
- Add tests for viewMode prop behavior

This fixes the "failed to fetch" stuck state after single-tool video upload.
Previously, JobList only fetched once on mount with useEffect([]), causing
it to get stuck in error state if the initial fetch failed.

Fixes #365
- Add videoPath state to VideoUpload component
- Skip upload if videoPath already set (re-use uploaded video)
- Clear videoPath when different file selected
- Show "Video uploaded" status indicator
- Add tests for videoPath re-use and file change behavior

This allows users to run multiple jobs on the same video without
re-uploading each time. The video is uploaded once and the path
is re-used for consecutive Run Job clicks.

Fixes #366
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds frontend state-flow documentation; prevents redundant job polling via an initialStatus prop on JobStatus; makes JobList conditional on a new viewMode prop; clears transient uploadResult/selectedJob at new video job start; adds tests and minor server logging.

Changes

Cohort / File(s) Summary
Frontend State Documentation
docs/design/frontend-state-flow.md
New design doc mapping global/component state, data loading flows, three polling locations, overlap scenarios, root causes and action items.
JobStatus polling & tests
web-ui/src/components/JobStatus.tsx, web-ui/src/components/JobStatus.test.tsx
Adds optional initialStatus?: Status prop; initialize polling state from it so terminal statuses skip HTTP polling; tests cover terminal/non-terminal and backward-compatible cases.
JobList view-mode fetch & tests
web-ui/src/components/JobList.tsx, web-ui/src/components/JobList.test.tsx
Adds optional viewMode?: string; fetch effect now depends on [viewMode] and skips fetching when viewMode is defined and not "jobs"; tests verify view transitions, retries and initial/missing behaviour.
App integration & run-job tests
web-ui/src/App.tsx, web-ui/src/App.run-job.test.tsx
Passes viewMode to JobList and initialStatus to JobStatus; handleRunVideoJob clears uploadResult and selectedJob prior to submission; adds integration test for consecutive job uploads and debug logs.
Server logging
server/app/api_routes/routes/jobs.py
Adds logger.info traces for incoming pagination, retrieved counts and returned item counts; no response/control-flow changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant User as User
  participant VideoComp as VideoUpload (web‑ui)
  participant App as App (web‑ui)
  participant API as Server API
  participant JobStatus as JobStatus (web‑ui)

  User->>VideoComp: select file
  User->>VideoComp: click "Run Job"
  VideoComp->>App: onRunJob(file, maybe videoPath)
  App->>App: clear uploadResult, selectedJob
  alt videoPath missing
    App->>API: POST /v1/video/upload
    API-->>App: { video_path }
  end
  App->>API: POST /v1/video/job
  API-->>App: { job_id, status }
  App->>JobStatus: render with initialStatus = job.status
  alt initialStatus is terminal
    Note right of JobStatus: no HTTP polling started
  else
    JobStatus->>API: poll GET /v1/jobs/:job_id (interval)
    API-->>JobStatus: status updates...
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 1 | ❌ 4

❌ Failed checks (3 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title mentions Issue #366 and describes skipping re-upload for consecutive runs, but the actual implementation covers broader state management fixes unrelated to the original issue scope. Update the PR title to accurately reflect all substantial changes, including JobList refetch, JobStatus polling fixes, and debug logging, or split into multiple focused PRs addressing each issue separately.
Description check ⚠️ Warning The PR description focuses exclusively on VideoUpload.tsx changes and Issue #366, but the actual changeset includes substantial modifications to JobList, JobStatus, App.tsx, and design documentation for Issues #363, #365, and #369. Expand the description to cover all modified files and linked issues (#363, #365, #366, #369), including JobList viewMode prop, JobStatus initialStatus prop, polling fixes, and App.tsx state clearing. Include TEST-CHANGE JUSTIFICATION for all test modifications.
Out of Scope Changes check ⚠️ Warning Significant changes appear out of scope: design documentation, JobList viewMode, JobStatus initialStatus, App.tsx debug logging, and server-side logging were not mentioned in Issue #366 objectives which focused narrowly on VideoUpload.tsx videoPath re-use. Either confirm these changes are intentional scope additions for other linked issues and update PR description accordingly, or move them to separate focused PRs addressing #363, #365, #369 individually.
Linked Issues check ❓ Inconclusive The changeset addresses multiple linked issues (#363, #365, #366, #369) but the PR description and title focus only on #366. JobStatus.initialStatus prop addresses #363; JobList.viewMode prop addresses #365; App.tsx clears state for #369; and design documentation details these fixes, but alignment clarity is missing. Clarify which linked issues this PR directly resolves versus documents. Explicitly state if this PR closes #363, #365, #366, #369, or if separate PRs address them. Update description to map changes to each issue requirement.
✅ Passed checks (1 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v0.16.4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
web-ui/src/components/VideoUpload.tsx (1)

153-156: Good file change detection, but consider edge case with modified files.

The comparison uses name and size to detect file changes. This works for typical use cases, but if a user modifies a file externally and re-selects it with the same name and size, the cached videoPath won't be cleared.

This is an acceptable trade-off for simplicity, but consider adding lastModified to the comparison for more robust detection if this becomes an issue.

♻️ Optional: Include lastModified in comparison
     // v0.16.4: Clear videoPath if different file selected
-    if (file && (f.name !== file.name || f.size !== file.size)) {
+    if (file && (f.name !== file.name || f.size !== file.size || f.lastModified !== file.lastModified)) {
       setVideoPath(null);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/VideoUpload.tsx` around lines 153 - 156, The current
file-change check only compares f.name and f.size and may miss externally
modified files with identical name/size; update the conditional in the
VideoUpload component to also compare f.lastModified (using the existing
variables file, f, videoPath and setVideoPath) so that when f.name !== file.name
|| f.size !== file.size || f.lastModified !== file.lastModified you clear the
cached videoPath by calling setVideoPath(null).
web-ui/src/components/JobList.tsx (1)

18-38: Consider resetting loading state when fetch is skipped.

When viewMode is not "jobs" (line 20), the effect returns early without resetting loading. If the component previously started loading data, loading might remain true after navigating away, causing a stale "Loading jobs..." message when returning to the jobs view before the new fetch completes.

♻️ Proposed fix to reset loading state
     useEffect(() => {
         // Issue `#365`: Only fetch when viewMode is 'jobs' (or not provided for backward compat)
-        if (viewMode !== undefined && viewMode !== "jobs") return;
+        if (viewMode !== undefined && viewMode !== "jobs") {
+            return;
+        }
 
+        setLoading(true);
         const loadJobs = async () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/JobList.tsx` around lines 18 - 38, The useEffect
currently returns early when viewMode is defined and not "jobs", leaving loading
possibly stuck true; update the effect to reset loading (call setLoading(false))
before the early return so the component doesn't show stale loading state when
switching away, e.g., in the effect that references viewMode and defines
loadJobs, invoke setLoading(false) right before the `return` and ensure
setLoading(true) is still called when starting loadJobs; modify the useEffect
that contains useEffect, viewMode, loadJobs, setLoading, setJobs, setError
accordingly.
docs/design/frontend-state-flow.md (1)

48-54: Add language specifiers to fenced code blocks.

Several code blocks lack language specifiers, which helps with syntax highlighting and accessibility. Consider adding text or appropriate language identifiers.

For example:

-```
+```text
 PluginSelector mount
   └─ useEffect([]) → apiClient.getPlugins()

Also applies to: 72-79, 102-108, 202-213, 237-249, 275-281, 306-312, 338-350, 353-361, 440-474

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/frontend-state-flow.md` around lines 48 - 54, The markdown fenced
code blocks in docs/design/frontend-state-flow.md (e.g., the block showing
"PluginSelector mount" with useEffect([]) → apiClient.getPlugins() and GET
/v1/plugins) are missing language specifiers; update each fenced block by adding
an appropriate language tag (use "text" for plain flow diagrams or a specific
language if applicable) to improve syntax highlighting and accessibility—apply
this change to the blocks around the ranges referenced (including those showing
useEffect, apiClient.getPlugins, and other flow snippets at 72-79, 102-108,
202-213, 237-249, 275-281, 306-312, 338-350, 353-361, 440-474).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/frontend-state-flow.md`:
- Around line 407-416: The "Root Causes" section has duplicate subsection
numbering: change the heading "### 3. Polling in Multiple Layers" (the one whose
text begins "Problem: App.tsx polls AND JobStatus.tsx polls. No coordination.")
to "### 4. Polling in Multiple Layers" so headings increment correctly and no
duplicate "3." appears; update only that heading text (the headings "Duplicate
Polling State" and "Polling in Multiple Layers" identify the locations).

---

Nitpick comments:
In `@docs/design/frontend-state-flow.md`:
- Around line 48-54: The markdown fenced code blocks in
docs/design/frontend-state-flow.md (e.g., the block showing "PluginSelector
mount" with useEffect([]) → apiClient.getPlugins() and GET /v1/plugins) are
missing language specifiers; update each fenced block by adding an appropriate
language tag (use "text" for plain flow diagrams or a specific language if
applicable) to improve syntax highlighting and accessibility—apply this change
to the blocks around the ranges referenced (including those showing useEffect,
apiClient.getPlugins, and other flow snippets at 72-79, 102-108, 202-213,
237-249, 275-281, 306-312, 338-350, 353-361, 440-474).

In `@web-ui/src/components/JobList.tsx`:
- Around line 18-38: The useEffect currently returns early when viewMode is
defined and not "jobs", leaving loading possibly stuck true; update the effect
to reset loading (call setLoading(false)) before the early return so the
component doesn't show stale loading state when switching away, e.g., in the
effect that references viewMode and defines loadJobs, invoke setLoading(false)
right before the `return` and ensure setLoading(true) is still called when
starting loadJobs; modify the useEffect that contains useEffect, viewMode,
loadJobs, setLoading, setJobs, setError accordingly.

In `@web-ui/src/components/VideoUpload.tsx`:
- Around line 153-156: The current file-change check only compares f.name and
f.size and may miss externally modified files with identical name/size; update
the conditional in the VideoUpload component to also compare f.lastModified
(using the existing variables file, f, videoPath and setVideoPath) so that when
f.name !== file.name || f.size !== file.size || f.lastModified !==
file.lastModified you clear the cached videoPath by calling setVideoPath(null).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4d97a787-d4d3-466c-8fee-a43e1c365d98

📥 Commits

Reviewing files that changed from the base of the PR and between 3fb7d78 and b658727.

📒 Files selected for processing (8)
  • docs/design/frontend-state-flow.md
  • web-ui/src/App.tsx
  • web-ui/src/components/JobList.test.tsx
  • web-ui/src/components/JobList.tsx
  • web-ui/src/components/JobStatus.test.tsx
  • web-ui/src/components/JobStatus.tsx
  • web-ui/src/components/VideoUpload.test.tsx
  • web-ui/src/components/VideoUpload.tsx

Comment thread docs/design/frontend-state-flow.md
Roger MT added 3 commits March 23, 2026 22:46
- Clear uploadResult and selectedJob at START of handleRunVideoJob
- Prevents old 'completed' status from flashing when running consecutive jobs
- Add TDD test for Issue #369
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
web-ui/src/App.run-job.test.tsx (1)

320-344: Make this suite’s mocks fully self-contained to avoid order coupling.

Line 320-344 does not initialise mockGetJob, so behaviour may depend on implementations left by earlier suites. Add explicit defaults in this beforeEach for determinism.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/App.run-job.test.tsx` around lines 320 - 344, The test suite's
beforeEach sets up many mocks but omits initialization of mockGetJob, which
leaves behavior dependent on other tests; update the beforeEach (the setup block
that currently initializes mockGetPlugins, mockGetPluginManifest,
mockSubmitVideoUpload, and mockListJobs) to explicitly set
mockGetJob.mockResolvedValue(...) to a sensible default (e.g., null or an empty
job object matching expected shape) so the suite is self-contained and
deterministic; ensure the chosen default aligns with how the tests assert job
state.
web-ui/src/App.tsx (1)

574-577: Gate navigation debug logs to development mode.

Line 575-577 and Line 583 will log on every tab click in production. Please gate these behind import.meta.env.DEV (or remove once validated).

♻️ Suggested tweak
               onClick={() => {
-                // Issue `#368`: Debug logging for navigation state changes
-                console.log("[NAV] viewMode changing:", viewMode, "->", mode);
-                console.log("[NAV] selectedJob before:", selectedJob?.job_id, selectedJob?.status);
+                if (import.meta.env.DEV) {
+                  console.log("[NAV] viewMode changing:", viewMode, "->", mode);
+                  console.log("[NAV] selectedJob before:", selectedJob?.job_id, selectedJob?.status);
+                }
                 setViewMode(mode);
                 // PERFORMANCE: Clear selectedJob when entering video modes
                 // to prevent dragging huge video_multi jobs into the video flow
                 // which would cause UI freeze in ResultsPanel
                 if (mode === "video-upload" || mode === "video-stream") {
                   setSelectedJob(null);
-                  console.log("[NAV] selectedJob cleared");
+                  if (import.meta.env.DEV) {
+                    console.log("[NAV] selectedJob cleared");
+                  }
                 }
               }}

Also applies to: 583-583

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/App.tsx` around lines 574 - 577, The debug console.log calls
around the navigation state change (referencing viewMode, mode, selectedJob and
the setViewMode call) should be gated to development only; wrap the console.log
statements (the ones before calling setViewMode and the other occurrence later)
in a runtime check using import.meta.env.DEV (e.g. if (import.meta.env.DEV) {
... }) so they do not run in production, or remove them entirely after
validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web-ui/src/App.run-job.test.tsx`:
- Around line 350-440: The test currently only checks mockSubmitVideoJob was
called twice but doesn't assert the UI/state was cleared immediately; update the
test around the second click (after fireEvent.click(runJobButton) and before
resolveSecondJob) to assert that the previous job UI/state is gone by checking
the component state or DOM: verify uploadResult and/or selectedJob are
null/empty (or that the previous job-id/status text is not present via
screen.queryByText or similar) immediately after the second click and before
resolving the second promise; keep references to runJobButton,
mockSubmitVideoJob, resolveSecondJob and the DOM queries used earlier to locate
where to add this assertion.

---

Nitpick comments:
In `@web-ui/src/App.run-job.test.tsx`:
- Around line 320-344: The test suite's beforeEach sets up many mocks but omits
initialization of mockGetJob, which leaves behavior dependent on other tests;
update the beforeEach (the setup block that currently initializes
mockGetPlugins, mockGetPluginManifest, mockSubmitVideoUpload, and mockListJobs)
to explicitly set mockGetJob.mockResolvedValue(...) to a sensible default (e.g.,
null or an empty job object matching expected shape) so the suite is
self-contained and deterministic; ensure the chosen default aligns with how the
tests assert job state.

In `@web-ui/src/App.tsx`:
- Around line 574-577: The debug console.log calls around the navigation state
change (referencing viewMode, mode, selectedJob and the setViewMode call) should
be gated to development only; wrap the console.log statements (the ones before
calling setViewMode and the other occurrence later) in a runtime check using
import.meta.env.DEV (e.g. if (import.meta.env.DEV) { ... }) so they do not run
in production, or remove them entirely after validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67d7b026-86a6-45cd-8a0a-370295ad9df9

📥 Commits

Reviewing files that changed from the base of the PR and between 06bee2a and ac5a6f0.

📒 Files selected for processing (2)
  • web-ui/src/App.run-job.test.tsx
  • web-ui/src/App.tsx

Comment thread web-ui/src/App.run-job.test.tsx
Roger MT added 2 commits March 24, 2026 05:33
- Add assertions to verify Job Processing section appears after first job
- Add critical assertion to verify old job UI is cleared BEFORE second response
- Add assertion to verify Job Processing reappears after second resolve
- Fix duplicate heading number in frontend-state-flow.md
- JobList.tsx: Set loading=false when viewMode is not 'jobs'
- JobList.tsx: Set loading=true when starting fetch
- frontend-state-flow.md: Update pollStatus description to reflect initialStatus fix
- frontend-state-flow.md: Mark action items as completed
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/frontend-state-flow.md`:
- Around line 48-54: The fenced code blocks in
docs/design/frontend-state-flow.md lack language identifiers (MD040); update
each triple-backtick fence (e.g., the block showing "PluginSelector mount" and
other blocks at ranges referenced: 48-54, 72-80, 102-108, 202-213, 237-249,
275-281, 306-312, 338-350, 353-361, 440-474) to include an appropriate language
tag such as text or typescript (e.g., ```text or ```typescript) so the linter
passes and readability improves; ensure you add the tag to both opening fences
for each block and keep content unchanged.
- Around line 167-194: Documentation is inconsistent about JobStatus.tsx initial
poll status: update the doc sections that state pollStatus always initializes to
"pending" (lines referenced around the old snippets) to reflect the current
behavior using initialStatus (i.e., that pollStatus now uses initialStatus to
preserve job state across remounts), and remove or correct the claims that
polling restarts on every remount; specifically reference JobStatus.tsx, the
pollStatus state and initialStatus prop/variable and adjust the paragraphs at
the impacted locations (around the earlier 170/192 and 471+ and 367-385 areas)
so the document consistently describes the new initialStatus-based
initialization and the conditions that stop polling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88fcef59-d1df-4eff-8331-1a13a0ecb590

📥 Commits

Reviewing files that changed from the base of the PR and between ac5a6f0 and db3a7df.

📒 Files selected for processing (2)
  • docs/design/frontend-state-flow.md
  • web-ui/src/App.run-job.test.tsx

Comment on lines +48 to +54
```
PluginSelector mount
└─ useEffect([]) → apiClient.getPlugins()
└─ GET /v1/plugins
├─ SUCCESS → setPlugins(data), setLoading(false)
└─ FAIL → setError(msg), setPlugins([])
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add language identifiers to fenced code blocks.

Several fences are missing a language tag (MD040). Please label them (for example text or typescript) to satisfy linting and improve readability.

🛠️ Example fix pattern
-```
+```text
 PluginSelector mount
   └─ useEffect([]) → apiClient.getPlugins()
 ...
-```
+```

-```
+```text
 ┌─────────────────────────────────────────────────────────────┐
 │                         App.tsx                              │
 ...
-```
+```

Also applies to: 72-80, 102-108, 202-213, 237-249, 275-281, 306-312, 338-350, 353-361, 440-474

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 48-48: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/frontend-state-flow.md` around lines 48 - 54, The fenced code
blocks in docs/design/frontend-state-flow.md lack language identifiers (MD040);
update each triple-backtick fence (e.g., the block showing "PluginSelector
mount" and other blocks at ranges referenced: 48-54, 72-80, 102-108, 202-213,
237-249, 275-281, 306-312, 338-350, 353-361, 440-474) to include an appropriate
language tag such as text or typescript (e.g., ```text or ```typescript) so the
linter passes and readability improves; ensure you add the tag to both opening
fences for each block and keep content unchanged.

Comment thread docs/design/frontend-state-flow.md Outdated
@rogermt rogermt added bug Something isn't working documentation Improvements or additions to documentation labels Mar 24, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web-ui/src/components/JobList.tsx (1)

29-44: ⚠️ Potential issue | 🟠 Major

Add retry/backoff around listJobs to handle transient failures.

apiClient.listJobs() is currently single-attempt. A temporary network/server blip goes straight to error state with no recovery attempt.

Suggested patch
         const loadJobs = async () => {
-            console.log("[JOBLIST] Fetching jobs...");
-            try {
-                const data = await apiClient.listJobs();
-                console.log("[JOBLIST] Success:", data.length, "jobs");
-                setJobs(data);
-                setError(null);
-            } catch (err) {
-                console.error("[JOBLIST] Error:", err);
-                setError(
-                    err instanceof Error ? err.message : "Failed to load jobs"
-                );
-                setJobs([]);
-            } finally {
-                setLoading(false);
-            }
+            console.log("[JOBLIST] Fetching jobs...");
+            const maxAttempts = 3;
+            let attempt = 0;
+            let delayMs = 400;
+
+            while (attempt < maxAttempts) {
+                try {
+                    const data = await apiClient.listJobs();
+                    console.log("[JOBLIST] Success:", data.length, "jobs");
+                    setJobs(data);
+                    setError(null);
+                    return;
+                } catch (err) {
+                    attempt += 1;
+                    if (attempt >= maxAttempts) {
+                        console.error("[JOBLIST] Error:", err);
+                        setError(
+                            err instanceof Error ? err.message : "Failed to load jobs"
+                        );
+                        setJobs([]);
+                        return;
+                    }
+                    await new Promise((resolve) => setTimeout(resolve, delayMs));
+                    delayMs *= 2;
+                } finally {
+                    if (attempt >= maxAttempts) setLoading(false);
+                }
+            }
         };

As per coding guidelines "Wrap all operations involving external services, APIs, or LLMs in retry logic to handle transient failures", "Implement retries with exponential backoff", and "Design systems with backup routes or default behaviors if a primary external call fails after retry attempts".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/JobList.tsx` around lines 29 - 44, The loadJobs
function currently calls apiClient.listJobs() once and fails immediately; modify
loadJobs to implement retry with exponential backoff (e.g., maxAttempts 3-5,
backoffFactor 2, baseDelay ~200-500ms) around apiClient.listJobs(), retrying on
transient/network errors and breaking early on non-retryable failures, and
ensure setJobs, setError and setLoading are updated only after final success or
final failure; use a small delay utility (or setTimeout/Promise) to implement
backoff and include cancellation/timeout awareness if available from apiClient.
♻️ Duplicate comments (2)
docs/design/frontend-state-flow.md (2)

276-283: ⚠️ Potential issue | 🟡 Minor

JobStatus behaviour is described with outdated polling-start logic.

These sections still state fresh mounts start from pollStatus="pending" for completed jobs, which conflicts with the initialStatus-based behaviour now in JobStatus.tsx.

Suggested wording direction
- JobStatus mounts with pollStatus="pending"
-   └─ Triggers JobStatus HTTP polling (BUG!)
+ JobStatus mounts with pollStatus initialised from initialStatus
+   └─ Polling starts only when status is non-terminal

Also applies to: 353-362

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/frontend-state-flow.md` around lines 276 - 283, Update the doc
text that describes JobStatus polling so it matches the current implementation:
replace claims that fresh mounts always start from pollStatus="pending" with
wording that JobStatus uses the initialStatus prop to decide whether to start
polling (i.e., only non-terminal initialStatus triggers polling), and adjust the
flow diagram entries referencing JobList onClick → onJobSelect(job) →
setSelectedJob and App.selectedJob polling and JobStatus mounts to reference
initialStatus instead of pollStatus; ensure the same correction is applied to
the duplicate section later (the block currently at the alternate location
describing the same logic).

48-54: ⚠️ Potential issue | 🟡 Minor

Add language identifiers to all fenced code blocks.

Several fences are still untyped, which keeps MD040 warnings active.

Fix pattern
-```
+```text
 PluginSelector mount
   └─ useEffect([]) → apiClient.getPlugins()
 ...
-```
+```

Also applies to: 72-80, 102-108, 203-214, 238-250, 276-283, 307-313, 339-351, 354-362, 441-475

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/frontend-state-flow.md` around lines 48 - 54, Several fenced code
blocks in the document are untyped (e.g., the "PluginSelector mount" block
shown) which triggers MD040; update every triple-backtick fence to include a
language identifier (use "text" for these flow diagrams) — e.g., change ``` to
```text for the shown block and the other untyped fences referenced (72-80,
102-108, 203-214, 238-250, 276-283, 307-313, 339-351, 354-362, 441-475) so all
fenced blocks have explicit language tags.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/frontend-state-flow.md`:
- Around line 102-115: The JobList flow in the diagram is wrong: it should
document a viewMode-triggered fetch (useEffect([viewMode])) instead of a
mount-only useEffect([]); update the sequence for JobList to show
useEffect([viewMode]) → apiClient.listJobs() with the same GET/Success/Fail
branches, change the "Polling Required" and "Dependencies" notes to reflect that
fetch is triggered by viewMode (not strictly mount-only), and mark Issue `#365` as
resolved/fixed in the diagram text; apply the same corrections to the other
occurrences referencing the JobList flow (the other two sections mentioned) so
all three places consistently reference viewMode-triggered fetching and Issue
`#365` state.

---

Outside diff comments:
In `@web-ui/src/components/JobList.tsx`:
- Around line 29-44: The loadJobs function currently calls apiClient.listJobs()
once and fails immediately; modify loadJobs to implement retry with exponential
backoff (e.g., maxAttempts 3-5, backoffFactor 2, baseDelay ~200-500ms) around
apiClient.listJobs(), retrying on transient/network errors and breaking early on
non-retryable failures, and ensure setJobs, setError and setLoading are updated
only after final success or final failure; use a small delay utility (or
setTimeout/Promise) to implement backoff and include cancellation/timeout
awareness if available from apiClient.

---

Duplicate comments:
In `@docs/design/frontend-state-flow.md`:
- Around line 276-283: Update the doc text that describes JobStatus polling so
it matches the current implementation: replace claims that fresh mounts always
start from pollStatus="pending" with wording that JobStatus uses the
initialStatus prop to decide whether to start polling (i.e., only non-terminal
initialStatus triggers polling), and adjust the flow diagram entries referencing
JobList onClick → onJobSelect(job) → setSelectedJob and App.selectedJob polling
and JobStatus mounts to reference initialStatus instead of pollStatus; ensure
the same correction is applied to the duplicate section later (the block
currently at the alternate location describing the same logic).
- Around line 48-54: Several fenced code blocks in the document are untyped
(e.g., the "PluginSelector mount" block shown) which triggers MD040; update
every triple-backtick fence to include a language identifier (use "text" for
these flow diagrams) — e.g., change ``` to ```text for the shown block and the
other untyped fences referenced (72-80, 102-108, 203-214, 238-250, 276-283,
307-313, 339-351, 354-362, 441-475) so all fenced blocks have explicit language
tags.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63114db4-e6cf-42b9-8551-31215c3ffe4c

📥 Commits

Reviewing files that changed from the base of the PR and between db3a7df and 945ef62.

📒 Files selected for processing (2)
  • docs/design/frontend-state-flow.md
  • web-ui/src/components/JobList.tsx

Comment on lines +102 to +115
```
viewMode === "jobs" → JobList renders
└─ useEffect([]) → apiClient.listJobs()
└─ GET /v1/jobs?limit=10&skip=0
├─ SUCCESS → setJobs(data), setLoading(false)
└─ FAIL → setError(msg), setJobs([])
```

**Dependencies:**
- Requires server running
- Does NOT require plugin to be loaded

**Polling Required:** NO - one-time fetch on mount

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

JobList fetch flow is internally inconsistent in the document.

The flow still documents mount-only useEffect([]) and marks Issue #365 as open, but the action list marks the fix as done. Please align all three sections to the current viewMode-triggered fetch behaviour.

Also applies to: 387-407, 482-483

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 102-102: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/frontend-state-flow.md` around lines 102 - 115, The JobList flow
in the diagram is wrong: it should document a viewMode-triggered fetch
(useEffect([viewMode])) instead of a mount-only useEffect([]); update the
sequence for JobList to show useEffect([viewMode]) → apiClient.listJobs() with
the same GET/Success/Fail branches, change the "Polling Required" and
"Dependencies" notes to reflect that fetch is triggered by viewMode (not
strictly mount-only), and mark Issue `#365` as resolved/fixed in the diagram text;
apply the same corrections to the other occurrences referencing the JobList flow
(the other two sections mentioned) so all three places consistently reference
viewMode-triggered fetching and Issue `#365` state.

@rogermt rogermt merged commit f4fd38b into main Mar 24, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Video Upload Re-use: Skip re-upload for consecutive runs on same file

1 participant