fix(ui): standardize conditional swr query enabling#1816
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR introduces a new app-stream SSE service for real-time file-system and state change notifications, replaces SSE-based update hooks across the UI with a centralized live-connection manager, adds workspace initial state to frontend configuration, and refactors query gating from Changes
Sequence Diagram(s)sequenceDiagram
participant Component as Component
participant LiveHook as useLiveDAG(s)
participant Manager as AppLiveManager
participant EventSource as EventSource
participant Mutate as Query Mutate
Note over Component,Mutate: Initial Setup
Component->>LiveHook: useEffect: subscribe
LiveHook->>Manager: subscribe(remoteNode, apiURL, callbacks)
Manager->>EventSource: EventSource(url + token)
EventSource-->>Manager: open
Manager->>Manager: emit 'connected' + optional 'reset'
Note over Component,Mutate: Live Event Flow
EventSource->>Manager: event: 'dag.changed' (JSON)
Manager->>Manager: parse + match event type
Manager->>LiveHook: callback(AppLiveEvent)
LiveHook->>LiveHook: debounce matched events
LiveHook->>Mutate: call mutate() (debounced)
Mutate->>Component: refresh query data
Note over Component,Mutate: Reconnection
EventSource->>Manager: onerror (disconnect)
Manager->>Manager: schedule reconnect (exponential backoff)
Manager->>EventSource: new EventSource after delay
EventSource-->>Manager: open
Manager->>Manager: emit 'reset' on reconnect
Manager->>LiveHook: callback(reset)
LiveHook->>Mutate: mutate() to refresh all
sequenceDiagram
participant Page as Page Component
participant Selector as TemplateSelector
participant Query as useQuery('/dags')
participant TagQuery as useQuery('/dags/tags')
participant API as REST API
Note over Page,API: Template Selector Open/Close Flow
Page->>Selector: onOpenChange callback
Selector->>Selector: setIsOpen(true)
Selector->>Query: whenEnabled(isOpen, {...init})
Query->>API: GET /dags
Selector->>TagQuery: whenEnabled(isOpen && isTagFilterOpen, {...})
Note over Selector,API: Filter Reset on Open
Selector->>Selector: handleOpen() → resetFilters()
Selector->>Selector: clear selectedDag, filters
Selector->>Page: onOpenChange(true)
Note over Selector,API: Close and Cleanup
Selector->>Selector: handleClose() → resetFilters()
Selector->>Query: whenEnabled(false, {...}) → null
Selector->>TagQuery: whenEnabled(false, {...}) → null
Selector->>Page: onOpenChange(false)
sequenceDiagram
participant FileSystem as Filesystem
participant Watcher as recursiveWatcher
participant Coalescer as appEventCoalescer
participant Hub as AppHub
participant Subscriber as Client (SSE)
Note over FileSystem,Subscriber: File Change → Event → Subscribers
FileSystem->>Watcher: file change event
Watcher->>Watcher: match DAG/doc/queue file
Watcher->>Coalescer: enqueue(AppEvent)
Coalescer->>Coalescer: debounce (coalsce multiple)
Coalescer->>Hub: broadcast(event)
Hub->>Hub: send to all buffered channels
Hub->>Subscriber: SSE frame: event: 'dag.changed'
Note over FileSystem,Subscriber: Watcher Error → Reset
FileSystem->>Watcher: onerror
Watcher->>Coalescer: enqueue(reset event)
Coalescer->>Hub: broadcast(reset)
Hub->>Subscriber: SSE frame: event: 'reset'
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes This PR spans significant architectural changes across backend and frontend layers with high heterogeneity. The backend introduces a new service with filesystem watching and pub/sub logic (~600 lines); the frontend establishes a new centralized live-connection manager (~340 lines) and refactors ~15+ components across multiple feature areas from SSE to live patterns. While individual component refactors follow a consistent pattern, the diversity of files, the density of logic in the new managers, and the interconnected dependency changes require careful reasoning across multiple layers and subsystems. Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (6)
internal/service/frontend/sse/app_stream_test.go (2)
37-56: Idempotency tests rely on implicit "no panic" assertion.The idempotency tests verify that calling
Stop()/Shutdown()twice doesn't panic, which is valid. Consider adding a comment clarifying the intent.♻️ Add clarifying comments
func TestRecursiveWatcherStopIsIdempotent(t *testing.T) { + // Calling Stop() multiple times should not panic watcher := &recursiveWatcher{ done: make(chan struct{}), } watcher.Stop() watcher.Stop() } func TestAppStreamServiceShutdownIsIdempotent(t *testing.T) { + // Calling Shutdown() multiple times should not panic service := &AppStreamService{🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/service/frontend/sse/app_stream_test.go` around lines 37 - 56, Add an inline comment to each idempotency test clarifying that the test asserts "no panic" by invoking the method twice: in TestRecursiveWatcherStopIsIdempotent, above the two watcher.Stop() calls note that the test ensures recursiveWatcher.Stop() is safe to call multiple times (no panic); and in TestAppStreamServiceShutdownIsIdempotent, add a similar comment above the two service.Shutdown() calls stating the test verifies AppStreamService.Shutdown() is idempotent (no panic) so readers understand the implicit assertion.
13-35: Consider using stretchr/testify assertions.Per coding guidelines, Go test files should use stretchr/testify assertions. This would improve readability and provide more descriptive failure messages.
♻️ Example refactor using testify
+import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + ... +) func TestWriteAppEventFrame(t *testing.T) { recorder := httptest.NewRecorder() err := writeAppEventFrame(recorder, AppEvent{...}) - if err != nil { - t.Fatalf("writeAppEventFrame() error = %v", err) - } + require.NoError(t, err) body := recorder.Body.String() - if !strings.Contains(body, "event: dag.changed\n") { - t.Fatalf("expected SSE event frame, got %q", body) - } + assert.Contains(t, body, "event: dag.changed\n") + assert.Contains(t, body, `"fileName":"example.yaml"`) + assert.Contains(t, body, `"reason":"updated"`) - ... }As per coding guidelines: "Use stretchr/testify assertions for testing in Go"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/service/frontend/sse/app_stream_test.go` around lines 13 - 35, Replace manual t.Fatalf checks in TestWriteAppEventFrame with stretchr/testify assertions: import "github.com/stretchr/testify/require" (or "assert"), call require.NoError(t, err) after writeAppEventFrame, and use require.Contains(t, body, "event: dag.changed\n"), require.Contains(t, body, `"fileName":"example.yaml"`), and require.Contains(t, body, `"reason":"updated"`) instead of the three if/ t.Fatalf blocks; update the test file imports accordingly and remove the now-unnecessary t.Fatalf branches in TestWriteAppEventFrame.ui/src/features/cockpit/hooks/useCockpitState.ts (1)
88-91:workspaceError: nullremoves error visibility.By always returning
nullforworkspaceError, consumers lose the ability to display workspace-related API errors. If the prior implementation exposed query errors, consider whether the UI still needs this capability.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/features/cockpit/hooks/useCockpitState.ts` around lines 88 - 91, The return value always sets workspaceError to null, removing upstream error visibility; update useCockpitState to return the actual error from the workspace query (e.g., replace workspaceError: null with workspaceError: workspacesQuery.error or whatever error variable the fetch hook exposes) so consumers (that read workspaces, selectedWorkspace) can render API errors; locate the error coming from the workspace fetch hook used in useCockpitState and pass it through as workspaceError.ui/src/features/cockpit/components/DateKanbanSection.tsx (1)
42-55: Consider typing the error more explicitly.The inline type assertion
(error as { message?: string } | undefined)?.messagesuggests the hook'serrortype may not be well-defined. IfuseDateKanbanDatareturns a typed error (e.g.,Error | null), the assertion could be removed.♻️ Cleaner error handling if hook returns `Error | null`
- <span className="text-destructive"> - {(error as { message?: string } | undefined)?.message || - 'Failed to load runs'} - </span> + <span className="text-destructive"> + {error instanceof Error ? error.message : 'Failed to load runs'} + </span>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/features/cockpit/components/DateKanbanSection.tsx` around lines 42 - 55, The component is using a loose inline cast for error; update the hook useDateKanbanData to return a properly typed error (e.g., Error | null) and then remove the cast in DateKanbanSection.tsx—use the typed error directly (e.g., error?.message || 'Failed to load runs') and keep the existing retry() call intact; ensure any callers and types for useDateKanbanData, and the component's props/state that consume it, are updated to reflect Error | null so the runtime check is safe and no inline assertion like (error as { message?: string } | undefined) is needed.ui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsPanel.tsx (1)
40-48: Simplify redundantliveEnabledcomputation.
liveEnabledfor the sub-DAG branch duplicates the checks already performed byisSubDAGRunon line 39. SinceisSubDAGRunisBoolean(subDAGRunId && parentDAGRunId && parentName), the ternary conditionBoolean(parentName && parentDAGRunId && subDAGRunId)is always true whenisSubDAGRunis true.♻️ Suggested simplification
- const liveEnabled = isSubDAGRun - ? Boolean(parentName && parentDAGRunId && subDAGRunId) - : Boolean(name && dagRunId); + const liveEnabled = isSubDAGRun || Boolean(name && dagRunId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsPanel.tsx` around lines 40 - 48, The ternary in liveEnabled redundantly re-checks the same condition encapsulated by isSubDAGRun; replace the ternary with a simple boolean expression: set liveEnabled = isSubDAGRun || Boolean(name && dagRunId) so sub-DAG runs use isSubDAGRun directly and non-sub-DAG runs still check name + dagRunId, leaving useLiveConnection(liveEnabled) unchanged.ui/src/hooks/AppLiveManager.ts (1)
293-312: Isolate subscriber exceptions in the shared fan-out.
notifySubscribers()andupdateState()invoke consumer callbacks inline. One throw there aborts delivery to the remaining subscribers and can bubble out of the shared live-update path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ui/src/hooks/AppLiveManager.ts` around lines 293 - 312, notifySubscribers and updateState currently call subscriber callbacks inline so one subscriber throwing aborts delivery to others; wrap each call to subscriber.onInvalidate(event) in notifySubscribers and subscriber.onStateChange(conn.state) in updateState with a try/catch to isolate exceptions, handle/log the error (using existing logger or console) and continue iterating so one bad subscriber cannot break the fan-out.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/service/frontend/sse/app_stream.go`:
- Around line 316-323: Compare normalized/cleaned paths when deciding the
createRoot boolean: call the same normalization used by uniqueNonEmptyPaths on
cfg.Paths.DAGsDir (e.g., filepath.Clean/absolute equivalent) and compare that
cleaned value to dagRoot (which is already cleaned) before passing the boolean
to newRecursiveWatcher; apply the same fix for the second occurrence around the
other watcher creation (lines 348-360) so createRoot is true when the logical
primary DAGsDir matches regardless of trailing slashes or relative paths.
- Around line 260-263: The code in the fsnotify Create handling and in the
directory traversal swallows errors from w.addDirRecursive (and the
filepath.Walk used inside it), leaving subtrees unwatched; modify the Create
branch to check and propagate the error from w.addDirRecursive (e.g., if err :=
w.addDirRecursive(event.Name); err != nil { return err } or send the error to
the component's existing reset/error channel) and likewise update the traversal
logic inside addDirRecursive (or the filepath.Walk callback) to return any walk
errors instead of discarding them so callers can detect and handle
watch-registration failures.
In `@ui/src/features/cockpit/components/TemplateSelector.tsx`:
- Around line 104-116: The effect in TemplateSelector.tsx clears selectedDag
whenever the dropdown is closed because it returns early on !isOpen and also
clears selectedDag when selectedTemplate exists but the menu is closed, causing
the label to disappear; update the useEffect that references
selectedTemplate/filteredDags/isOpen so it only sets selectedDag(null) when
selectedTemplate is falsy and always tries to find and set the matching dag from
filteredDags (i.e., remove the early return on isOpen), and apply the same
change to the similar effect around lines 163-171 so the preselected
selectedTemplate keeps its label even when the dropdown is closed (refer to
selectedDag, selectedTemplate, filteredDags, isOpen in the effect).
In `@ui/src/features/cockpit/hooks/useCockpitState.ts`:
- Around line 60-70: The POST call to create a workspace using client.POST
currently swallows failures; update the createWorkspace flow to surface failures
by handling the returned error: when client.POST('/workspaces', ...) returns an
error, log it (e.g., via console.error or an existing logger), and either return
the error to callers or set a local error state so the UI can display it; keep
the existing success path that calls applyWorkspaces(...) and
selectWorkspace(...), but ensure the error branch does not silently
no-op—propagate or persist the error for user feedback.
In
`@ui/src/features/dag-runs/components/dag-run-details/__tests__/DAGRunDetailsPanel.test.tsx`:
- Around line 1-8: Add the required GPL v3 license header to the top of this
test file (DAGRunDetailsPanel.test.tsx) above the imports: include the standard
GPL v3 notice (or SPDX identifier such as "SPDX-License-Identifier:
GPL-3.0-or-later") plus the copyright/owner line used across the repo so the
file legally matches other sources; ensure the header sits before any code
(imports like React, render, MemoryRouter, etc.) so linters and tooling
recognize it.
In `@ui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsModal.tsx`:
- Around line 73-75: The current gate makes dagRunQueryEnabled require a truthy
dagRunId, but the code later intentionally falls back to 'latest', so change the
condition for dagRunQueryEnabled to allow queries when name is present even if
dagRunId is empty (i.e., use isOpen && !canQuerySubDag && Boolean(name) instead
of requiring dagRunId); apply the same fix to the other occurrence around the
second block (the lines referenced 95-100) so both checks permit the 'latest'
fallback to run.
In
`@ui/src/features/dags/components/chat-history/__tests__/StepMessagesTable.test.tsx`:
- Around line 1-6: This new test file (StepMessagesTable.test.tsx) is missing
the required GPL v3 license header; add the standard GPL v3 license header to
the top of the file (above the imports) either by running the repository helper
(make addlicense) or by inserting the project’s canonical GPL v3 header manually
so the file header matches other source files and CI checks; ensure the header
is placed before the existing imports and saved.
In `@ui/src/hooks/AppLiveManager.ts`:
- Line 1: This new file AppLiveManager.ts is missing the project's required GPL
v3 license header; add the standard GPL header to the top of AppLiveManager.ts
(the file that currently imports getAuthToken) and then run the repository task
to apply/verify headers by executing make addlicense before merging so the file
conforms to the **/*.{go,ts,tsx,js} GPL header policy.
In `@ui/src/hooks/useAppLive.ts`:
- Line 1: This new file (useAppLive.ts — seen by the import line "import {
useContext, useEffect, useRef, useState } from 'react';") is missing the
required GPL v3 license header; run the repository's license tool (make
addlicense) in the ui/src directory (or project root as configured) to insert
the standard GPL header for TypeScript files, then recommit the updated file so
it conforms to the coding guideline for **/*.{go,ts,tsx,js}.
In `@ui/test-results/.last-run.json`:
- Around line 1-6: The committed JSON contains transient test-run state keys
"status" and "failedTests" and should not be versioned; remove the file from the
PR (delete the committed .last-run.json), add an ignore rule to your repo's
.gitignore to exclude the test-results/.last-run.json pattern (or the
test-results directory’s transient artifacts), and if you need a deterministic
example add a checked-in fixture file under a clearly named fixtures path
instead of this runtime output.
In
`@ui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md`:
- Around line 1-154: Delete the committed run-specific artifact
ui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md
from the repo and add a rule for ui/test-results/ to .gitignore so future
transient test outputs aren’t tracked; remove the file from version control
(ensure it’s deleted/unstaged from the index) and commit the updated .gitignore
and deletion together, and verify no source or tests reference error-context.md
before pushing.
---
Nitpick comments:
In `@internal/service/frontend/sse/app_stream_test.go`:
- Around line 37-56: Add an inline comment to each idempotency test clarifying
that the test asserts "no panic" by invoking the method twice: in
TestRecursiveWatcherStopIsIdempotent, above the two watcher.Stop() calls note
that the test ensures recursiveWatcher.Stop() is safe to call multiple times (no
panic); and in TestAppStreamServiceShutdownIsIdempotent, add a similar comment
above the two service.Shutdown() calls stating the test verifies
AppStreamService.Shutdown() is idempotent (no panic) so readers understand the
implicit assertion.
- Around line 13-35: Replace manual t.Fatalf checks in TestWriteAppEventFrame
with stretchr/testify assertions: import "github.com/stretchr/testify/require"
(or "assert"), call require.NoError(t, err) after writeAppEventFrame, and use
require.Contains(t, body, "event: dag.changed\n"), require.Contains(t, body,
`"fileName":"example.yaml"`), and require.Contains(t, body,
`"reason":"updated"`) instead of the three if/ t.Fatalf blocks; update the test
file imports accordingly and remove the now-unnecessary t.Fatalf branches in
TestWriteAppEventFrame.
In `@ui/src/features/cockpit/components/DateKanbanSection.tsx`:
- Around line 42-55: The component is using a loose inline cast for error;
update the hook useDateKanbanData to return a properly typed error (e.g., Error
| null) and then remove the cast in DateKanbanSection.tsx—use the typed error
directly (e.g., error?.message || 'Failed to load runs') and keep the existing
retry() call intact; ensure any callers and types for useDateKanbanData, and the
component's props/state that consume it, are updated to reflect Error | null so
the runtime check is safe and no inline assertion like (error as { message?:
string } | undefined) is needed.
In `@ui/src/features/cockpit/hooks/useCockpitState.ts`:
- Around line 88-91: The return value always sets workspaceError to null,
removing upstream error visibility; update useCockpitState to return the actual
error from the workspace query (e.g., replace workspaceError: null with
workspaceError: workspacesQuery.error or whatever error variable the fetch hook
exposes) so consumers (that read workspaces, selectedWorkspace) can render API
errors; locate the error coming from the workspace fetch hook used in
useCockpitState and pass it through as workspaceError.
In `@ui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsPanel.tsx`:
- Around line 40-48: The ternary in liveEnabled redundantly re-checks the same
condition encapsulated by isSubDAGRun; replace the ternary with a simple boolean
expression: set liveEnabled = isSubDAGRun || Boolean(name && dagRunId) so
sub-DAG runs use isSubDAGRun directly and non-sub-DAG runs still check name +
dagRunId, leaving useLiveConnection(liveEnabled) unchanged.
In `@ui/src/hooks/AppLiveManager.ts`:
- Around line 293-312: notifySubscribers and updateState currently call
subscriber callbacks inline so one subscriber throwing aborts delivery to
others; wrap each call to subscriber.onInvalidate(event) in notifySubscribers
and subscriber.onStateChange(conn.state) in updateState with a try/catch to
isolate exceptions, handle/log the error (using existing logger or console) and
continue iterating so one bad subscriber cannot break the fan-out.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5492cda0-af71-4e24-8bd7-9c2fa35addaa
📒 Files selected for processing (54)
internal/service/frontend/server.gointernal/service/frontend/sse/app_stream.gointernal/service/frontend/sse/app_stream_test.gointernal/service/frontend/templates.gointernal/service/frontend/templates/base.gohtmlinternal/service/frontend/templates_test.goui/index.htmlui/src/contexts/ConfigContext.tsxui/src/features/agent/hooks/__tests__/useAgentChat.test.tsxui/src/features/cockpit/components/CockpitToolbar.tsxui/src/features/cockpit/components/DAGPreviewModal.tsxui/src/features/cockpit/components/DateKanbanList.tsxui/src/features/cockpit/components/DateKanbanSection.tsxui/src/features/cockpit/components/TemplateSelector.tsxui/src/features/cockpit/components/__tests__/DAGPreviewModal.test.tsxui/src/features/cockpit/components/__tests__/DateKanbanList.test.tsxui/src/features/cockpit/components/__tests__/TemplateSelector.test.tsxui/src/features/cockpit/hooks/__tests__/useInfiniteKanban.test.tsxui/src/features/cockpit/hooks/useCockpitDagRuns.tsui/src/features/cockpit/hooks/useCockpitState.tsui/src/features/cockpit/hooks/useDateKanbanData.tsui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsModal.tsxui/src/features/dag-runs/components/dag-run-details/DAGRunDetailsPanel.tsxui/src/features/dag-runs/components/dag-run-details/__tests__/DAGRunDetailsPanel.test.tsxui/src/features/dag-runs/components/dag-run-list/__tests__/DAGRunTable.test.tsxui/src/features/dags/components/chat-history/StepMessagesTable.tsxui/src/features/dags/components/chat-history/__tests__/StepMessagesTable.test.tsxui/src/features/dags/components/common/InlineLogViewer.tsxui/src/features/dags/components/dag-details/DAGDetailsContent.tsxui/src/features/dags/components/dag-details/DAGDetailsPanel.tsxui/src/features/dags/components/dag-details/DAGDetailsSidePanel.tsxui/src/features/dags/components/dag-details/SubDAGRunsList.tsxui/src/features/dags/components/dag-details/__tests__/DAGDetailsSidePanel.test.tsxui/src/features/dags/components/dag-editor/DAGSpec.tsxui/src/features/dags/components/dag-execution/DAGExecutionHistory.tsxui/src/features/dags/components/dag-execution/ExecutionLog.tsxui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsxui/src/features/dags/components/dag-execution/StepLog.tsxui/src/hooks/AppLiveManager.tsui/src/hooks/__tests__/queryUtils.test.tsui/src/hooks/api.tsui/src/hooks/queryUtils.tsui/src/hooks/useAppLive.tsui/src/pages/cockpit/index.tsxui/src/pages/dag-runs/dag-run/index.tsxui/src/pages/dag-runs/index.tsxui/src/pages/dags/dag/index.tsxui/src/pages/dags/index.tsxui/src/pages/docs/components/DocEditor.tsxui/src/pages/docs/index.tsxui/src/pages/index.tsxui/src/pages/queues/index.tsxui/test-results/.last-run.jsonui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md
💤 Files with no reviewable changes (1)
- ui/src/features/cockpit/hooks/useCockpitDagRuns.ts
| if event.Op&fsnotify.Create != 0 { | ||
| if info, err := os.Stat(event.Name); err == nil && info.IsDir() { | ||
| _ = w.addDirRecursive(event.Name) | ||
| } |
There was a problem hiding this comment.
Bubble up recursive watch-registration failures.
Line 262 drops addDirRecursive errors, and Lines 275-277 swallow traversal errors from the walk. That leaves parts of the tree unwatched without any reset/error signal, so invalidations under those directories can silently stop.
Suggested fix
if event.Op&fsnotify.Create != 0 {
if info, err := os.Stat(event.Name); err == nil && info.IsDir() {
- _ = w.addDirRecursive(event.Name)
+ if err := w.addDirRecursive(event.Name); err != nil {
+ w.onReset(fmt.Sprintf("failed to register watcher for %s: %v", event.Name, err))
+ }
}
}
@@
func (w *recursiveWatcher) addDirRecursive(root string) error {
return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
if err != nil {
- return nil
+ return err
}
if !d.IsDir() {
return nil
}
return w.watcher.Add(path)Also applies to: 273-281
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/service/frontend/sse/app_stream.go` around lines 260 - 263, The code
in the fsnotify Create handling and in the directory traversal swallows errors
from w.addDirRecursive (and the filepath.Walk used inside it), leaving subtrees
unwatched; modify the Create branch to check and propagate the error from
w.addDirRecursive (e.g., if err := w.addDirRecursive(event.Name); err != nil {
return err } or send the error to the component's existing reset/error channel)
and likewise update the traversal logic inside addDirRecursive (or the
filepath.Walk callback) to return any walk errors instead of discarding them so
callers can detect and handle watch-registration failures.
| paths := uniqueNonEmptyPaths( | ||
| cfg.Paths.DAGsDir, | ||
| cfg.Paths.AltDAGsDir, | ||
| ) | ||
| for _, dagRoot := range paths { | ||
| service.watchers = append(service.watchers, newRecursiveWatcher( | ||
| dagRoot, | ||
| dagRoot == cfg.Paths.DAGsDir, |
There was a problem hiding this comment.
Normalize DAGsDir before deciding createRoot.
uniqueNonEmptyPaths cleans the configured paths, but Line 323 compares the cleaned dagRoot to the raw cfg.Paths.DAGsDir. A value like /var/lib/dagu/dags/ or ./dags flips createRoot to false, so the primary DAG root is no longer auto-created and the watcher quietly no-ops when that directory is missing.
Suggested fix
+ primaryDAGRoot := ""
+ if cfg.Paths.DAGsDir != "" {
+ primaryDAGRoot = filepath.Clean(cfg.Paths.DAGsDir)
+ }
+
paths := uniqueNonEmptyPaths(
cfg.Paths.DAGsDir,
cfg.Paths.AltDAGsDir,
)
for _, dagRoot := range paths {
service.watchers = append(service.watchers, newRecursiveWatcher(
dagRoot,
- dagRoot == cfg.Paths.DAGsDir,
+ dagRoot == primaryDAGRoot,
service.handleDAGFileEvent,
service.publishReset,
))
}Also applies to: 348-360
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@internal/service/frontend/sse/app_stream.go` around lines 316 - 323, Compare
normalized/cleaned paths when deciding the createRoot boolean: call the same
normalization used by uniqueNonEmptyPaths on cfg.Paths.DAGsDir (e.g.,
filepath.Clean/absolute equivalent) and compare that cleaned value to dagRoot
(which is already cleaned) before passing the boolean to newRecursiveWatcher;
apply the same fix for the second occurrence around the other watcher creation
(lines 348-360) so createRoot is true when the logical primary DAGsDir matches
regardless of trailing slashes or relative paths.
| useEffect(() => { | ||
| if (!selectedTemplate) { | ||
| setSelectedDag(null); | ||
| return; | ||
| } | ||
| if (!isOpen) { | ||
| return; | ||
| } | ||
| const found = filteredDags.find((d) => d.fileName === selectedTemplate); | ||
| if (found) { | ||
| setSelectedDag(found); | ||
| } | ||
| }, [selectedTemplate, filteredDags]); | ||
| }, [filteredDags, isOpen, selectedTemplate]); |
There was a problem hiding this comment.
Keep the selected template label available while the dropdown is closed.
selectedDag is only rehydrated while the menu is open, then it gets cleared again on remote-node changes. A preselected selectedTemplate can therefore render as “Select template...” until the user reopens the selector, which makes the current selection look lost.
Also applies to: 163-171
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/src/features/cockpit/components/TemplateSelector.tsx` around lines 104 -
116, The effect in TemplateSelector.tsx clears selectedDag whenever the dropdown
is closed because it returns early on !isOpen and also clears selectedDag when
selectedTemplate exists but the menu is closed, causing the label to disappear;
update the useEffect that references selectedTemplate/filteredDags/isOpen so it
only sets selectedDag(null) when selectedTemplate is falsy and always tries to
find and set the matching dag from filteredDags (i.e., remove the early return
on isOpen), and apply the same change to the similar effect around lines 163-171
so the preselected selectedTemplate keeps its label even when the dropdown is
closed (refer to selectedDag, selectedTemplate, filteredDags, isOpen in the
effect).
| const { data, error } = await client.POST('/workspaces', { | ||
| params: { query: { remoteNode } }, | ||
| body: { name }, | ||
| }); | ||
| if (!error) { | ||
| mutate(); | ||
| selectWorkspace(name); | ||
| if (!error && data) { | ||
| applyWorkspaces((prev) => { | ||
| const next = [...prev.filter((ws) => ws.id !== data.id), data]; | ||
| return next.sort((a, b) => a.name.localeCompare(b.name)); | ||
| }); | ||
| selectWorkspace(data.name); | ||
| } |
There was a problem hiding this comment.
Silently ignoring errors in createWorkspace may hide failures.
When the POST request fails, the error is not surfaced or logged. Users won't know why the workspace wasn't created.
🛡️ Consider surfacing or logging errors
const { data, error } = await client.POST('/workspaces', {
params: { query: { remoteNode } },
body: { name },
});
- if (!error && data) {
+ if (error) {
+ console.error('Failed to create workspace:', error);
+ return;
+ }
+ if (data) {
applyWorkspaces((prev) => {Alternatively, consider returning the error or updating a local error state that the UI can display.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { data, error } = await client.POST('/workspaces', { | |
| params: { query: { remoteNode } }, | |
| body: { name }, | |
| }); | |
| if (!error) { | |
| mutate(); | |
| selectWorkspace(name); | |
| if (!error && data) { | |
| applyWorkspaces((prev) => { | |
| const next = [...prev.filter((ws) => ws.id !== data.id), data]; | |
| return next.sort((a, b) => a.name.localeCompare(b.name)); | |
| }); | |
| selectWorkspace(data.name); | |
| } | |
| const { data, error } = await client.POST('/workspaces', { | |
| params: { query: { remoteNode } }, | |
| body: { name }, | |
| }); | |
| if (error) { | |
| console.error('Failed to create workspace:', error); | |
| return; | |
| } | |
| if (data) { | |
| applyWorkspaces((prev) => { | |
| const next = [...prev.filter((ws) => ws.id !== data.id), data]; | |
| return next.sort((a, b) => a.name.localeCompare(b.name)); | |
| }); | |
| selectWorkspace(data.name); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/src/features/cockpit/hooks/useCockpitState.ts` around lines 60 - 70, The
POST call to create a workspace using client.POST currently swallows failures;
update the createWorkspace flow to surface failures by handling the returned
error: when client.POST('/workspaces', ...) returns an error, log it (e.g., via
console.error or an existing logger), and either return the error to callers or
set a local error state so the UI can display it; keep the existing success path
that calls applyWorkspaces(...) and selectWorkspace(...), but ensure the error
branch does not silently no-op—propagate or persist the error for user feedback.
| import React from 'react'; | ||
| import { render, screen } from '@testing-library/react'; | ||
| import { MemoryRouter } from 'react-router-dom'; | ||
| import { afterEach, describe, expect, it, vi } from 'vitest'; | ||
| import { AppBarContext } from '@/contexts/AppBarContext'; | ||
| import { useQuery } from '@/hooks/api'; | ||
| import { useLiveConnection } from '@/hooks/useAppLive'; | ||
| import DAGRunDetailsPanel from '../DAGRunDetailsPanel'; |
There was a problem hiding this comment.
Missing GPL v3 license header.
This file is missing the required license header. As per coding guidelines, source files should include GPL v3 license headers.
🔧 Add license header
+// Copyright (C) 2026 Yota Hamada
+// SPDX-License-Identifier: GPL-3.0-or-later
+
import React from 'react';
import { render, screen } from '@testing-library/react';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import React from 'react'; | |
| import { render, screen } from '@testing-library/react'; | |
| import { MemoryRouter } from 'react-router-dom'; | |
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { AppBarContext } from '@/contexts/AppBarContext'; | |
| import { useQuery } from '@/hooks/api'; | |
| import { useLiveConnection } from '@/hooks/useAppLive'; | |
| import DAGRunDetailsPanel from '../DAGRunDetailsPanel'; | |
| // Copyright (C) 2026 Yota Hamada | |
| // SPDX-License-Identifier: GPL-3.0-or-later | |
| import React from 'react'; | |
| import { render, screen } from '@testing-library/react'; | |
| import { MemoryRouter } from 'react-router-dom'; | |
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { AppBarContext } from '@/contexts/AppBarContext'; | |
| import { useQuery } from '@/hooks/api'; | |
| import { useLiveConnection } from '@/hooks/useAppLive'; | |
| import DAGRunDetailsPanel from '../DAGRunDetailsPanel'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@ui/src/features/dag-runs/components/dag-run-details/__tests__/DAGRunDetailsPanel.test.tsx`
around lines 1 - 8, Add the required GPL v3 license header to the top of this
test file (DAGRunDetailsPanel.test.tsx) above the imports: include the standard
GPL v3 notice (or SPDX identifier such as "SPDX-License-Identifier:
GPL-3.0-or-later") plus the copyright/owner line used across the repo so the
file legally matches other sources; ensure the header sits before any code
(imports like React, render, MemoryRouter, etc.) so linters and tooling
recognize it.
| import React from 'react'; | ||
| import { cleanup, render, screen } from '@testing-library/react'; | ||
| import { afterEach, describe, expect, it, vi } from 'vitest'; | ||
| import { AppBarContext } from '@/contexts/AppBarContext'; | ||
| import { useQuery } from '@/hooks/api'; | ||
| import { StepMessagesTable } from '../StepMessagesTable'; |
There was a problem hiding this comment.
Add GPL v3 license header to new test file.
This is a new file and is missing the required GPL v3 license header. As per coding guidelines, source files should have GPL v3 license headers managed via make addlicense.
📜 Proposed fix to add license header
+// Copyright (C) 2026 Yota Hamada
+// SPDX-License-Identifier: GPL-3.0-or-later
+
import React from 'react';
import { cleanup, render, screen } from '@testing-library/react';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import React from 'react'; | |
| import { cleanup, render, screen } from '@testing-library/react'; | |
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { AppBarContext } from '@/contexts/AppBarContext'; | |
| import { useQuery } from '@/hooks/api'; | |
| import { StepMessagesTable } from '../StepMessagesTable'; | |
| // Copyright (C) 2026 Yota Hamada | |
| // SPDX-License-Identifier: GPL-3.0-or-later | |
| import React from 'react'; | |
| import { cleanup, render, screen } from '@testing-library/react'; | |
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { AppBarContext } from '@/contexts/AppBarContext'; | |
| import { useQuery } from '@/hooks/api'; | |
| import { StepMessagesTable } from '../StepMessagesTable'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@ui/src/features/dags/components/chat-history/__tests__/StepMessagesTable.test.tsx`
around lines 1 - 6, This new test file (StepMessagesTable.test.tsx) is missing
the required GPL v3 license header; add the standard GPL v3 license header to
the top of the file (above the imports) either by running the repository helper
(make addlicense) or by inserting the project’s canonical GPL v3 header manually
so the file header matches other source files and CI checks; ensure the header
is placed before the existing imports and saved.
| @@ -0,0 +1,337 @@ | |||
| import { getAuthToken } from '@/lib/authHeaders'; | |||
There was a problem hiding this comment.
Add the standard GPL header to this new file.
Please run make addlicense here before merging.
As per coding guidelines, **/*.{go,ts,tsx,js}: Apply GPL v3 license headers on source files, managed via make addlicense.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/src/hooks/AppLiveManager.ts` at line 1, This new file AppLiveManager.ts is
missing the project's required GPL v3 license header; add the standard GPL
header to the top of AppLiveManager.ts (the file that currently imports
getAuthToken) and then run the repository task to apply/verify headers by
executing make addlicense before merging so the file conforms to the
**/*.{go,ts,tsx,js} GPL header policy.
| @@ -0,0 +1,241 @@ | |||
| import { useContext, useEffect, useRef, useState } from 'react'; | |||
There was a problem hiding this comment.
Add the standard GPL header to this new file.
Please run make addlicense here before merging.
As per coding guidelines, **/*.{go,ts,tsx,js}: Apply GPL v3 license headers on source files, managed via make addlicense.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/src/hooks/useAppLive.ts` at line 1, This new file (useAppLive.ts — seen by
the import line "import { useContext, useEffect, useRef, useState } from
'react';") is missing the required GPL v3 license header; run the repository's
license tool (make addlicense) in the ui/src directory (or project root as
configured) to insert the standard GPL header for TypeScript files, then
recommit the updated file so it conforms to the coding guideline for
**/*.{go,ts,tsx,js}.
| { | ||
| "status": "failed", | ||
| "failedTests": [ | ||
| "1b112854bd09624bf1f9-fa870e5e77ba8b4c5af0" | ||
| ] | ||
| } No newline at end of file |
There was a problem hiding this comment.
Do not commit transient test-run state files.
This file captures local/CI runtime state (status: failed) and will cause noisy, non-deterministic diffs across runs. Please remove it from the PR and ignore ui/test-results/.last-run.json in VCS unless intentionally versioned as a fixture.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@ui/test-results/.last-run.json` around lines 1 - 6, The committed JSON
contains transient test-run state keys "status" and "failedTests" and should not
be versioned; remove the file from the PR (delete the committed .last-run.json),
add an ignore rule to your repo's .gitignore to exclude the
test-results/.last-run.json pattern (or the test-results directory’s transient
artifacts), and if you need a deterministic example add a checked-in fixture
file under a clearly named fixtures path instead of this runtime output.
| # Page snapshot | ||
|
|
||
| ```yaml | ||
| - generic [ref=e4]: | ||
| - complementary [ref=e5]: | ||
| - navigation [ref=e7]: | ||
| - generic [ref=e8]: | ||
| - generic [ref=e9]: | ||
| - button "Collapse sidebar" [ref=e10]: | ||
| - generic: | ||
| - generic: D | ||
| - img [ref=e12] | ||
| - generic [ref=e14]: Dagu | ||
| - navigation [ref=e15]: | ||
| - combobox [ref=e17]: | ||
| - generic [ref=e18]: | ||
| - img | ||
| - generic [ref=e19]: local | ||
| - img | ||
| - generic [ref=e20]: | ||
| - generic [ref=e21]: | ||
| - generic [ref=e22]: Overview | ||
| - link "Cockpit" [ref=e24] [cursor=pointer]: | ||
| - /url: /cockpit | ||
| - img [ref=e27] | ||
| - generic [ref=e30]: Cockpit | ||
| - link "Dashboard" [ref=e32] [cursor=pointer]: | ||
| - /url: /dashboard | ||
| - img [ref=e34] | ||
| - generic [ref=e35]: Dashboard | ||
| - link "Docs" [ref=e37] [cursor=pointer]: | ||
| - /url: /docs | ||
| - img [ref=e39] | ||
| - generic [ref=e42]: Docs | ||
| - generic [ref=e43]: | ||
| - generic [ref=e44]: Workflows | ||
| - link "Definitions" [ref=e46] [cursor=pointer]: | ||
| - /url: /dags | ||
| - img [ref=e48] | ||
| - generic [ref=e53]: Definitions | ||
| - link "Runs" [ref=e55] [cursor=pointer]: | ||
| - /url: /dag-runs | ||
| - img [ref=e57] | ||
| - generic [ref=e61]: Runs | ||
| - link "Queues" [ref=e63] [cursor=pointer]: | ||
| - /url: /queues | ||
| - img [ref=e65] | ||
| - generic [ref=e68]: Queues | ||
| - link "Search" [ref=e70] [cursor=pointer]: | ||
| - /url: /search | ||
| - img [ref=e72] | ||
| - generic [ref=e75]: Search | ||
| - link "Base Config" [ref=e77] [cursor=pointer]: | ||
| - /url: /base-config | ||
| - img [ref=e79] | ||
| - generic [ref=e91]: Base Config | ||
| - generic [ref=e92]: | ||
| - generic [ref=e93]: Settings | ||
| - link "System Status" [ref=e95] [cursor=pointer]: | ||
| - /url: /system-status | ||
| - img [ref=e97] | ||
| - generic [ref=e99]: System Status | ||
| - link "Remote Nodes" [ref=e101] [cursor=pointer]: | ||
| - /url: /remote-nodes | ||
| - img [ref=e103] | ||
| - generic [ref=e106]: Remote Nodes | ||
| - link "Audit Logs (Pro)" [ref=e108] [cursor=pointer]: | ||
| - /url: /audit-logs | ||
| - img [ref=e110] | ||
| - generic [ref=e113]: Audit Logs (Pro) | ||
| - generic [ref=e114]: | ||
| - button "Agent" [ref=e116]: | ||
| - img [ref=e118] | ||
| - generic [ref=e121]: Agent | ||
| - img [ref=e123] | ||
| - generic [ref=e125]: | ||
| - link "Settings" [ref=e127] [cursor=pointer]: | ||
| - /url: /agent-settings | ||
| - img [ref=e129] | ||
| - generic [ref=e132]: Settings | ||
| - link "Memory" [ref=e134] [cursor=pointer]: | ||
| - /url: /agent-memory | ||
| - img [ref=e136] | ||
| - generic [ref=e144]: Memory | ||
| - link "Skills" [ref=e146] [cursor=pointer]: | ||
| - /url: /agent-skills | ||
| - img [ref=e148] | ||
| - generic [ref=e150]: Skills | ||
| - link "Souls" [ref=e152] [cursor=pointer]: | ||
| - /url: /agent-souls | ||
| - img [ref=e154] | ||
| - generic [ref=e156]: Souls | ||
| - generic [ref=e157]: | ||
| - generic [ref=e158]: Admin | ||
| - link "License" [ref=e160] [cursor=pointer]: | ||
| - /url: /license | ||
| - img [ref=e162] | ||
| - generic [ref=e164]: License | ||
| - generic [ref=e165]: | ||
| - generic [ref=e166]: | ||
| - button "Agent" [ref=e167]: | ||
| - img [ref=e169] | ||
| - generic [ref=e171]: Agent | ||
| - button "Dark Mode" [ref=e172]: | ||
| - img [ref=e174] | ||
| - generic [ref=e176]: Dark Mode | ||
| - generic [ref=e177]: vv2.3.1-11-ge4e4c088 | ||
| - main [ref=e179]: | ||
| - generic [ref=e180]: | ||
| - generic [ref=e181]: | ||
| - text: "Update available: vv2.3.1-11-ge4e4c088 → v2.3.1" | ||
| - link "View release" [ref=e182] [cursor=pointer]: | ||
| - /url: https://github.com/dagu-org/dagu/releases | ||
| - generic [ref=e183]: | ||
| - text: · Run | ||
| - code [ref=e184]: dagu upgrade | ||
| - text: to update | ||
| - button "Dismiss update notification" [ref=e185]: | ||
| - img [ref=e186] | ||
| - generic [ref=e190]: | ||
| - generic [ref=e191]: | ||
| - combobox [ref=e193]: | ||
| - generic: All workspaces | ||
| - img | ||
| - button "Select template..." [ref=e195] [cursor=pointer]: | ||
| - img [ref=e196] | ||
| - generic [ref=e199]: Select template... | ||
| - img [ref=e200] | ||
| - generic [ref=e202]: | ||
| - generic [ref=e203]: | ||
| - heading "2026-03-21 Sat" [level=2] [ref=e205] | ||
| - generic [ref=e206]: No runs | ||
| - generic [ref=e207]: | ||
| - heading "2026-03-20 Fri" [level=2] [ref=e209] | ||
| - generic [ref=e210]: Loading runs... | ||
| - generic [ref=e211]: | ||
| - heading "2026-03-19 Thu" [level=2] [ref=e213] | ||
| - generic [ref=e214]: Loading runs... | ||
| - generic [ref=e215]: | ||
| - heading "2026-03-18 Wed" [level=2] [ref=e217] | ||
| - generic [ref=e218]: Loading runs... | ||
| - generic [ref=e219]: | ||
| - heading "2026-03-17 Tue" [level=2] [ref=e221] | ||
| - generic [ref=e222]: Loading runs... | ||
| - generic [ref=e223]: | ||
| - heading "2026-03-16 Mon" [level=2] [ref=e225] | ||
| - generic [ref=e226]: Loading runs... | ||
| - generic [ref=e227]: | ||
| - heading "2026-03-15 Sun" [level=2] [ref=e229] | ||
| - generic [ref=e230]: Loading runs... | ||
| - generic [ref=e231]: | ||
| - heading "2026-03-14 Sat" [level=2] [ref=e233] | ||
| - generic [ref=e234]: Loading runs... | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether test-results artifacts are intended to be versioned.
# Expected: either explicit allowlist in tests/docs, or ignore patterns for transient outputs.
set -euo pipefail
echo "== Search for references to ui/test-results as committed fixtures =="
rg -n -C2 'ui/test-results|test-results/.last-run|error-context.md' README.md .gitignore .eslintignore .prettierignore package.json pnpm-workspace.yaml || true
echo
echo "== Find ignore rules for test-results artifacts =="
rg -n -C2 'test-results|\\.last-run\\.json|error-context\\.md' .gitignore **/.gitignore || trueRepository: dagu-org/dagu
Length of output: 417
🏁 Script executed:
# Check if the file exists and is tracked in git
git ls-files | grep -E 'ui/test-results|error-context' || echo "No matches in git index"
echo "---"
# Check git status for this file
git status --short | grep -E 'test-results|error-context' || echo "No staged/unstaged changes for these files"
echo "---"
# Read .gitignore to see actual ignore patterns
echo "=== .gitignore content ==="
cat .gitignore || echo ".gitignore not found"Repository: dagu-org/dagu
Length of output: 779
🏁 Script executed:
# Check if test infrastructure references or expects these files
rg -i 'test-results|error-context|\.last-run' --type-list 2>/dev/null | head -20 || true
echo "---"
# Search in source code for references
rg -i 'test-results|error-context' . --type py --type go --type ts --type js 2>/dev/null | head -20 || echo "No source code references found"
echo "---"
# Check if there are any docs about UI testing or test artifacts
find . -maxdepth 3 -name '*test*.md' -o -name '*ui*.md' 2>/dev/null | grep -v node_modules | head -10 || trueRepository: dagu-org/dagu
Length of output: 64
Remove committed test artifacts and add ignore rules.
The file ui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md is run-specific debug output that should not be committed to the repository. It contains volatile data (timestamps, version strings, UI element references) and is not referenced by any source code or test infrastructure.
Additionally, add ui/test-results/ to .gitignore to prevent similar transient artifacts from being accidentally committed in the future.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@ui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md`
around lines 1 - 154, Delete the committed run-specific artifact
ui/test-results/dagu-cockpit-autoupdate-ch-dc750--run-without-manual-refresh/error-context.md
from the repo and add a rule for ui/test-results/ to .gitignore so future
transient test outputs aren’t tracked; remove the file from version control
(ensure it’s deleted/unstaged from the index) and commit the updated .gitignore
and deletion together, and verify no source or tests reference error-context.md
before pushing.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1816 +/- ##
==========================================
+ Coverage 69.18% 69.22% +0.03%
==========================================
Files 426 426
Lines 51421 51421
==========================================
+ Hits 35576 35595 +19
+ Misses 12827 12809 -18
+ Partials 3018 3017 -1
... and 12 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
Summary
Testing
Summary by CodeRabbit
Release Notes
New Features
Tests