feat(all): Add support for multiple-level nested local DAGs#1493
Conversation
WalkthroughAdds an optional query parameter Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Browser as Browser (UI)
participant UI as Frontend Components
participant Client as Frontend API Client
participant FE as Frontend Server (GetSubDAGRuns)
participant Store as Status Store / DB
Browser->>UI: open ParallelExecutionModal / SubDAGRunsList (rootDagName, rootDagRunId[, parentDagRunId])
UI->>Client: GET /dag-runs/{name}/{dagRunId}/sub-dag-runs?parentSubDAGRunId=...
Client->>FE: request with parentSubDAGRunId
FE->>Store: FindSubDAGRunStatus(parentSubDAGRunId) [if provided]
Store-->>FE: sub-dag-run status or not found
alt parent found
FE->>Store: GetSavedStatus(rootDagName, rootDagRunId)
Store-->>FE: root status + sub-run details
else parent not provided
FE->>Store: GetSavedStatus(rootDagName, rootDagRunId)
Store-->>FE: root status + sub-run details
end
FE-->>Client: aggregated sub-run list (includes statuses)
Client-->>UI: sub-run payload
UI->>UI: compute status counts, apply filter, render filtered list (map filtered index → original index)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used📓 Path-based instructions (1)**/*.go📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2025-12-04T10:34:17.062ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
🔇 Additional comments (2)
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: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
internal/runtime/executor/dag_runner.go (1)
503-539: Handle write errors consistently when aggregating local DAG YAML
createTempDAGFilecorrectly aggregates all local DAG YAMLs into a single temp file, but it ignores the error fromtempFile.WriteString("---\n"). In the (rare) case of an I/O failure, you’d silently produce a malformed file without propagating the error, which can make failures harder to diagnose.You’re already doing the right thing for the subsequent
Write; applying the same pattern to the separator keeps behavior consistent and safer.Proposed fix for separator write error handling
func createTempDAGFile(dagName string, yamlData []byte, localDAGs map[string]*core.DAG) (string, error) { @@ // Write the YAML data if _, err := tempFile.Write(yamlData); err != nil { _ = os.Remove(tempFile.Name()) return "", fmt.Errorf("failed to write YAML data: %w", err) } // Add other local DAG data to the temporary file for _, localDAG := range localDAGs { if localDAG.Name == dagName { continue } - tempFile.WriteString("---\n") - if _, err := tempFile.Write(localDAG.YamlData); err != nil { + if _, err := tempFile.WriteString("---\n"); err != nil { + _ = os.Remove(tempFile.Name()) + return "", fmt.Errorf("failed to write YAML separator: %w", err) + } + if _, err := tempFile.Write(localDAG.YamlData); err != nil { _ = os.Remove(tempFile.Name()) return "", fmt.Errorf("failed to write YAML data: %w", err) } } return tempFile.Name(), nil }ui/src/features/dags/components/dag-details/NodeStatusTableRow.tsx (2)
963-974: Same fallback inconsistency as desktop view.The mobile view has the same inconsistent fallback handling as identified in the desktop view (lines 588-600). Line 967 provides a fallback for
rootDagRunIdbut Line 966 does not provide one forrootDagName, and Line 965 removed the fallback fordagRunId.Apply the same fix as suggested for the desktop view to maintain consistency between both views.
Suggested fix
<SubDAGRunsList dagName={dagRun.name} - dagRunId={dagRun.dagRunId} - rootDagName={dagRun.rootDAGRunName} + dagRunId={dagRun.dagRunId || ''} + rootDagName={dagRun.rootDAGRunName || dagRun.name} rootDagRunId={dagRun.rootDAGRunId || ''} subDagName={subDagName || ''} allSubRuns={allSubRuns}
588-600: Inconsistent fallback handling for root DAG and DAG run properties.Props passed to
SubDAGRunsListare inconsistent. Line 593 provides a fallback forrootDagRunId(dagRun.rootDAGRunId || '') but line 592 does not provide a fallback forrootDagName(dagRun.rootDAGRunName), even though both are required string props in the component's type definition. Similarly, line 591 passesdagRunId={dagRun.dagRunId}without a fallback.While the schema marks these as required fields, the defensive check at line 424 suggests they can be undefined at runtime. Add consistent fallbacks to ensure type safety:
<SubDAGRunsList dagName={dagRun.name} - dagRunId={dagRun.dagRunId} - rootDagName={dagRun.rootDAGRunName} + dagRunId={dagRun.dagRunId || ''} + rootDagName={dagRun.rootDAGRunName || dagRun.name} rootDagRunId={dagRun.rootDAGRunId || ''} subDagName={subDagName || ''} allSubRuns={allSubRuns}ui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsx (1)
166-175: Edge case: modulo with zero when no sub-runs match the filter.When
filteredSubRuns.lengthis 0, the modulo operation causes unexpected behavior (NaN or Infinity). Add a guard to prevent navigation when the list is empty.Suggested fix
case 'ArrowDown': e.preventDefault(); + if (filteredSubRuns.length === 0) break; setSelectedIndex((prev) => prev === null ? 0 : (prev + 1) % filteredSubRuns.length); break; case 'ArrowUp': e.preventDefault(); + if (filteredSubRuns.length === 0) break; setSelectedIndex((prev) => prev === null ? filteredSubRuns.length - 1 : (prev - 1 + filteredSubRuns.length) % filteredSubRuns.length); break;
🧹 Nitpick comments (7)
ui/src/menu.tsx (1)
44-52: Redundant conditional: both branches apply the same class.The ternary on lines 47-48 evaluates to
text-primary-foregroundregardless ofisActive. Either remove the conditional or apply distinct styling for the active state if differentiation is intended.🔎 Proposed fix
<span className={cn( - 'flex items-center justify-center w-5 h-5 transform-none text-primary-foreground', - isActive ? 'text-primary-foreground' : 'text-primary-foreground' + 'flex items-center justify-center w-5 h-5 transform-none text-primary-foreground' )} > {children} </span>api/v2/api.yaml (1)
1485-1498: Sub‑DAG filtering parameter looks consistent and well‑documentedThe new
parentSubDAGRunIdquery parameter and the updated description clearly convey the multi‑level nested behavior and match existing naming patterns. This is a clean extension of the public API surface.If you end up reusing this parameter on other endpoints in the future, consider promoting it into
components.parametersfor reuse, but it’s fine as-is for a single use site.ui/src/api/v2/schema.ts (1)
575-586: TS client update correctly reflects the new OpenAPI parameterThe updated JSDoc on
/dag-runs/{name}/{dagRunId}/sub-dag-runsand theparentSubDAGRunId?: stringaddition togetSubDAGRuns.parameters.queryare in sync with the OpenAPI spec and keep the client surface straightforward to consume.Just ensure this file continues to be regenerated from
api/v2/api.yaml(rather than hand‑edited) so it stays aligned with future spec changes.Also applies to: 3256-3263
internal/runtime/executor/dag_runner.go (1)
60-84: Local sub‑DAG execution now correctly carries all inline DAG definitionsPassing
rCtx.DAG.LocalDAGsintocreateTempDAGFileensures the temp file for a local sub‑DAG contains not just the child DAG’s YAML but also its sibling inline DAGs, which is what enables the 3‑level nesting scenarios you’re testing.One thing to be aware of: in the distributed path (
BuildCoordinatorTask/dispatchToCoordinator), you still sendstring(e.DAG.YamlData)only. That means multi‑level nesting for inline DAGs is currently guaranteed only for the local execution path; if you ever attach aWorkerSelectorto an inline DAG and rely on distributed sub‑DAG runs there, you’ll likely need a similar YAML aggregation strategy for the task payload as well.Also applies to: 171-185
ui/src/features/dags/components/dag-details/SubDAGRunsList.tsx (1)
131-142: Unstable dependency:nodeSubRunIdsSet recreated every render.
nodeSubRunIdsis a newSetinstance on every render (line 97), causingstatusLabelMapto recompute unnecessarily. Consider memoizing the Set or using a stable representation.Suggested fix
- // Create a map of dagRunIds that belong to THIS node - const nodeSubRunIds = new Set(allSubRuns.map((sr) => sr.dagRunId)); + // Create a map of dagRunIds that belong to THIS node (memoized for stable reference) + const nodeSubRunIds = useMemo( + () => new Set(allSubRuns.map((sr) => sr.dagRunId)), + [allSubRuns] + );ui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsx (2)
15-30: Consider extracting shared constants to reduce duplication.
StatusFilterValuetype andSTATUS_DISPLAY_LABELSconstant are duplicated between this file andSubDAGRunsList.tsx. Consider extracting to a shared location for maintainability.
198-212: Auto-scroll may reference wrong child element after filtering.
container.children[selectedIndex]assumes children matchfilteredSubRunsindices. If the container has additional child elements (like the empty state message), this could scroll to the wrong element or fail silently.Consider using
data-indexattributes andquerySelectorfor more robust element targeting:Suggested fix
React.useEffect(() => { if (selectedIndex !== null && scrollContainerRef.current) { const container = scrollContainerRef.current; - const selectedElement = container.children[selectedIndex] as HTMLElement; + const selectedElement = container.querySelector(`[data-index="${selectedIndex}"]`) as HTMLElement; if (selectedElement) { selectedElement.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } } }, [selectedIndex]);Then add
data-index={displayIndex}to the rendered items at line 272.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
ui/favicon.icois excluded by!**/*.icoui/src/assets/images/logo_dark.pngis excluded by!**/*.png
📒 Files selected for processing (12)
api/v2/api.gen.go(3 hunks)api/v2/api.yaml(1 hunks)internal/integration/subdag_test.go(1 hunks)internal/runtime/executor/dag_runner.go(3 hunks)internal/service/frontend/api/v2/dagruns.go(3 hunks)ui/src/api/v2/schema.ts(2 hunks)ui/src/features/dags/components/DAGStatus.tsx(1 hunks)ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx(0 hunks)ui/src/features/dags/components/dag-details/NodeStatusTableRow.tsx(2 hunks)ui/src/features/dags/components/dag-details/SubDAGRunsList.tsx(7 hunks)ui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsx(5 hunks)ui/src/menu.tsx(3 hunks)
💤 Files with no reviewable changes (1)
- ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx
🧰 Additional context used
📓 Path-based instructions (5)
{api/v1,api/v2}/**/*.{proto,yaml,yml,json}
📄 CodeRabbit inference engine (AGENTS.md)
API definitions live in
api/v1andapi/v2; generated server stubs land ininternal/service, while matching TypeScript clients flow intoui/src/api
Files:
api/v2/api.yaml
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Backend entrypoint incmd/orchestrates the scheduler and CLI; runtime, persistence, and service layers sit underinternal/*(for exampleinternal/runtime,internal/persistence)
Keep Go filesgofmt/goimportsclean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, andErr...names for package-level errors
Repository linting relies ongolangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers ininternal/common
Files:
api/v2/api.gen.gointernal/runtime/executor/dag_runner.gointernal/service/frontend/api/v2/dagruns.gointernal/integration/subdag_test.go
ui/**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (ui/CLAUDE.md)
ui/**/*.{ts,tsx,jsx,js}: Use developer-centric UI design with high information density, minimal whitespace, compact components, and no unnecessary decorations
Support both light and dark modes for all UI components using Tailwind CSS class pairs likedark:bg-slate-700
NEVER use full-page loading overlays or LoadingIndicator components that hide content - show stale data while fetching updates instead
Use compact modal design with small headers, minimal padding (p-2 or p-3), tight spacing, and support keyboard navigation (arrows, enter, escape)
Use small heights for form elements: select boxes h-7 or smaller, buttons h-7 or h-8, inputs with compact padding (py-0.5 or py-1)
Minimize row heights in tables and lists while maintaining readability, merge related columns, and always handle long text withwhitespace-normal break-words
Use consistent metadata styling withbg-slate-200 dark:bg-slate-700backgrounds and maintain text hierarchy with primary/secondary/muted text colors
Use flexbox-first layouts withmin-h-0andoverflow-hiddento prevent layout breaks, account for fixed elements when setting heights
Maintain keyboard navigation support in all interactive components with appropriate focus indicators and ARIA labels
Files:
ui/src/features/dags/components/DAGStatus.tsxui/src/api/v2/schema.tsui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsxui/src/features/dags/components/dag-details/SubDAGRunsList.tsxui/src/features/dags/components/dag-details/NodeStatusTableRow.tsxui/src/menu.tsx
ui/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
ui/**/*.{ts,tsx}: The React + TypeScript frontend resides inui/, with production bundles copied tointernal/service/frontend/assetsbymake ui
UI code follows ESLint + Prettier (2-space indent) and Tailwind utilities; name React components in PascalCase (JobList.tsx) and hooks withuse*(useJobs.ts)
Files:
ui/src/features/dags/components/DAGStatus.tsxui/src/api/v2/schema.tsui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsxui/src/features/dags/components/dag-details/SubDAGRunsList.tsxui/src/features/dags/components/dag-details/NodeStatusTableRow.tsxui/src/menu.tsx
**/*_test.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*_test.go: Co-locate Go tests as*_test.go; favour table-driven cases and cover failure paths
Usestretchr/testify/requireand shared fixtures frominternal/testinstead of duplicating mocks
Files:
internal/integration/subdag_test.go
🧠 Learnings (4)
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: Applies to **/*_test.go : Co-locate Go tests as `*_test.go`; favour table-driven cases and cover failure paths
Applied to files:
internal/integration/subdag_test.go
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: `make test` (or `make test-coverage`) executes the Go suite via `gotestsum`; append `TEST_TARGET=./internal/...` to focus packages
Applied to files:
internal/integration/subdag_test.go
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: Applies to ui/**/*.{ts,tsx} : The React + TypeScript frontend resides in `ui/`, with production bundles copied to `internal/service/frontend/assets` by `make ui`
Applied to files:
ui/src/menu.tsx
📚 Learning: 2025-12-04T10:34:01.045Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: ui/CLAUDE.md:0-0
Timestamp: 2025-12-04T10:34:01.045Z
Learning: Applies to ui/**/*.{ts,tsx,jsx,js} : Minimize row heights in tables and lists while maintaining readability, merge related columns, and always handle long text with `whitespace-normal break-words`
Applied to files:
ui/src/menu.tsx
🧬 Code graph analysis (4)
api/v2/api.gen.go (1)
api/v1/api.gen.go (1)
InvalidParamFormatError(1166-1169)
internal/runtime/executor/dag_runner.go (1)
internal/core/dag.go (1)
DAG(33-135)
internal/service/frontend/api/v2/dagruns.go (3)
api/v2/api.gen.go (3)
GetSubDAGRunsRequestObject(5444-5448)GetSubDAGRunsResponseObject(5450-5452)GetSubDAGRuns404JSONResponse(5465-5465)internal/core/execution/dagrun.go (1)
NewDAGRunRef(168-173)internal/core/execution/runstatus.go (1)
DAGRunStatus(35-57)
internal/integration/subdag_test.go (3)
internal/test/helper.go (1)
Setup(81-225)internal/core/dag.go (1)
DAG(33-135)internal/runtime/agent/agent.go (1)
Agent(49-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build
- GitHub Check: Test on ubuntu-latest
🔇 Additional comments (11)
ui/src/menu.tsx (3)
2-2: LGTM!The import of
useConfigfrom ConfigContext appropriately enables dynamic title rendering from the application configuration.
96-98: LGTM!The collapsed state correctly displays the first letter of the configured title with an appropriate fallback to 'Dagu'.
103-109: LGTM!The expanded state correctly displays the full configured title with consistent fallback handling.
api/v2/api.gen.go (3)
1291-1297: NewParentSubDAGRunIdquery param wiring looks correctOptional
ParentSubDAGRunId *stringis added with consistentform/jsontags and a clear doc comment; this matches how other query params are modeled and should integrate cleanly with the rest of the handler stack.
3053-3067:parentSubDAGRunIdbinding matches existing error‑handling patternThe new
BindQueryParametercall forparentSubDAGRunIdfollows the same style and error‑handling (InvalidParamFormatError) as other optional query params in this file. No issues with required/optional semantics or naming.
8473-8666: RegeneratedswaggerSpecblob is expected for the API surface changeThe base64‑encoded, gzipped
swaggerSpecchunk has been updated, which is expected after modifyingapi/v2/api.yaml. As long as this file was regenerated viaoapi-codegenfrom the updated spec (rather than edited manually), this change looks good.internal/service/frontend/api/v2/dagruns.go (2)
871-889: Correct: Using root reference for all sub-DAG run detail lookups.Using
rootRefforgetSubDAGRunDetailcalls ensures consistent lookup from the root DAG's storage, which is the correct approach for multi-level nested DAGs where all sub-runs are stored under the root.
828-860: Implementation correctly supports multi-level nested DAG lookups.The
GetSubDAGRunsfunction properly branches based on theParentSubDAGRunIdparameter to handle both root DAG runs and nested sub-DAG runs. TheGetSubDAGRunsParamsstruct includes the requiredParentSubDAGRunIdfield, and error messages appropriately reflect the root/DAG context when performing status lookups. The function correctly iterates through both regular and repeated sub-runs, passingrootReftogetSubDAGRunDetailfor consistent storage access.ui/src/features/dags/components/dag-details/SubDAGRunsList.tsx (2)
63-78: Good: API call correctly uses root DAG context with optional parent sub-DAG run ID.The implementation correctly:
- Uses
rootDagNameandrootDagRunIdfor API path parameters- Conditionally passes
parentSubDAGRunIdonly for nested sub-DAGs- Aligns with the backend changes for multi-level nested DAG support
248-278: Filter UI follows design guidelines.The status filter buttons use compact styling (
px-1.5 py-0.5), support both light and dark modes, and only appear when there are multiple status types to filter. Good use of conditional rendering to reduce UI clutter.ui/src/features/dags/components/dag-execution/ParallelExecutionModal.tsx (1)
64-86: API integration correctly handles nested sub-DAG context.The
useQuerycall properly:
- Uses root DAG identifiers for the API path
- Conditionally passes
parentSubDAGRunIdfor nested scenarios- Pauses fetching when modal is closed
- Uses a reasonable 3-second refresh interval
| rootDagName={dagRun.rootDAGRunName} | ||
| rootDagRunId={dagRun.rootDAGRunId} | ||
| parentDagRunId={dagRun.dagRunId} |
There was a problem hiding this comment.
Potential type mismatch: optional values passed to required props.
dagRun.rootDAGRunName and dagRun.rootDAGRunId may be undefined for root DAG runs, but ParallelExecutionModal declares rootDagName and rootDagRunId as required string props. This could lead to runtime issues or TypeScript errors.
Consider providing fallback values:
Suggested fix
- rootDagName={dagRun.rootDAGRunName}
- rootDagRunId={dagRun.rootDAGRunId}
+ rootDagName={dagRun.rootDAGRunName || dagRun.name}
+ rootDagRunId={dagRun.rootDAGRunId || dagRun.dagRunId}
parentDagRunId={dagRun.dagRunId}📝 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.
| rootDagName={dagRun.rootDAGRunName} | |
| rootDagRunId={dagRun.rootDAGRunId} | |
| parentDagRunId={dagRun.dagRunId} | |
| rootDagName={dagRun.rootDAGRunName || dagRun.name} | |
| rootDagRunId={dagRun.rootDAGRunId || dagRun.dagRunId} | |
| parentDagRunId={dagRun.dagRunId} |
🤖 Prompt for AI Agents
In ui/src/features/dags/components/DAGStatus.tsx around lines 401 to 403,
dagRun.rootDAGRunName and dagRun.rootDAGRunId can be undefined for root DAG runs
while ParallelExecutionModal requires string props; fix by either passing safe
fallbacks (e.g., use the nullish coalescing operator to pass '' or
dagRun.dagRunId when root values are missing) or update ParallelExecutionModal
prop types to accept optional strings and handle undefined inside the
component—pick one approach and apply consistently where the modal is invoked.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
internal/integration/localdag_test.go (1)
111-165: LGTM! Multi-level nested local DAG support verified.The test correctly validates that multi-level nested local DAGs (root → middle-dag → leaf-dag) now execute successfully. The assertions verify:
- Successful execution without errors
- Root node status is
NodeSucceeded- Middle-dag created a sub-run
The implicit verification (overall success proves leaf-dag executed) is acceptable for this integration test level.
Optional: Consider verifying leaf-dag execution explicitly
For more thorough verification of the 3-level nesting, you could optionally retrieve and verify the middle-dag's sub-run to confirm leaf-dag executed:
// Verify middle-dag has a sub-run require.Len(t, dagRunStatus.Nodes[0].SubRuns, 1, "middle-dag should have one sub-run") + +// Optionally verify the leaf-dag execution within middle-dag's sub-run +middleDAGSubRun := dagRunStatus.Nodes[0].SubRuns[0] +require.Equal(t, core.Succeeded, middleDAGSubRun.Status, "middle-dag sub-run should succeed") +// Could further verify leaf-dag is in middle-dag's sub-runs if that level of detail is trackedHowever, the current level of verification is sufficient for confirming the feature works end-to-end.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
internal/integration/localdag_test.go(2 hunks)internal/runtime/executor/dag_runner_test.go(2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Backend entrypoint incmd/orchestrates the scheduler and CLI; runtime, persistence, and service layers sit underinternal/*(for exampleinternal/runtime,internal/persistence)
Keep Go filesgofmt/goimportsclean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, andErr...names for package-level errors
Repository linting relies ongolangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers ininternal/common
Files:
internal/integration/localdag_test.gointernal/runtime/executor/dag_runner_test.go
**/*_test.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*_test.go: Co-locate Go tests as*_test.go; favour table-driven cases and cover failure paths
Usestretchr/testify/requireand shared fixtures frominternal/testinstead of duplicating mocks
Files:
internal/integration/localdag_test.gointernal/runtime/executor/dag_runner_test.go
🧠 Learnings (1)
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: Applies to **/*_test.go : Co-locate Go tests as `*_test.go`; favour table-driven cases and cover failure paths
Applied to files:
internal/integration/localdag_test.go
🧬 Code graph analysis (2)
internal/integration/localdag_test.go (1)
internal/core/status.go (3)
Succeeded(11-11)NodeSucceeded(57-57)Status(4-4)
internal/runtime/executor/dag_runner_test.go (1)
internal/core/dag.go (1)
DAG(33-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build
- GitHub Check: Test on ubuntu-latest
🔇 Additional comments (2)
internal/runtime/executor/dag_runner_test.go (2)
302-321: LGTM! Backward compatibility maintained.The update correctly adapts the test to the new three-parameter
createTempDAGFilesignature by passingnilforlocalDAGs. The comment clearly documents this is for single-DAG scenarios, and all original assertions remain valid.
323-361: LGTM! Comprehensive test for multi-DAG aggregation.This new test thoroughly verifies the core feature of aggregating multiple local DAG YAMLs into a single temporary file. The test correctly:
- Creates a
localDAGsmap with parent and child DAGs- Verifies the output file contains both DAG definitions
- Confirms the YAML document separator (
---) is present- Validates the filename pattern
The test structure follows coding guidelines and provides good coverage for the new functionality.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
internal/runtime/executor/dag_runner.go (1)
504-548: Verify if deterministic local DAG order matters.The function correctly consolidates multiple local DAGs into a single multi-document YAML file. However, the iteration over the
localDAGsmap (lines 529-541) produces a non-deterministic order since Go maps have random iteration order.If the DAG loader or execution logic is sensitive to the order of local DAG definitions, this could cause reproducibility or debugging issues.
Optional: ensure deterministic order by sorting
If deterministic order is required, consider sorting the local DAG names before iteration:
// Add other local DAG data to the temporary file var names []string for name := range localDAGs { if name != dagName { names = append(names, name) } } sort.Strings(names) for _, name := range names { localDAG := localDAGs[name] if _, err := tempFile.WriteString("---\n"); err != nil { writeErr = err goto Fail } if _, err := tempFile.Write(localDAG.YamlData); err != nil { writeErr = err goto Fail } }This would require importing
sortat the top of the file.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
internal/runtime/executor/dag_runner.go(3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Backend entrypoint incmd/orchestrates the scheduler and CLI; runtime, persistence, and service layers sit underinternal/*(for exampleinternal/runtime,internal/persistence)
Keep Go filesgofmt/goimportsclean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, andErr...names for package-level errors
Repository linting relies ongolangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers ininternal/common
Files:
internal/runtime/executor/dag_runner.go
🧬 Code graph analysis (1)
internal/runtime/executor/dag_runner.go (1)
internal/core/dag.go (1)
DAG(33-135)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test on ubuntu-latest
- GitHub Check: Build
🔇 Additional comments (1)
internal/runtime/executor/dag_runner.go (1)
66-66: LGTM: Local DAGs propagation for multi-level nesting.Passing
rCtx.DAG.LocalDAGstocreateTempDAGFilecorrectly enables consolidation of all local DAG definitions into the temporary file, which is essential for multi-level nested DAG support.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
internal/integration/subdag_test.go (1)
427-452: Unused params declaration in worker-dag.The
worker-dagdeclaresTASK_IDandTASK_NAMEparams (lines 438-439) but they're never passed when calling it (line 433). This appears to be leftover from copying theParallelExecutiontest. Consider removing the unused params for clarity.🔎 Proposed fix
t.Run("PartialSuccessSubDAG", func(t *testing.T) { th := test.Setup(t) testDAG := th.DAG(t, ` steps: - name: parallel-tasks call: worker-dag --- name: worker-dag -params: - - TASK_ID - - TASK_NAME steps: - command: exit 1 continueOn: failure: true - exit 0 `)
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
internal/integration/localdag_test.go(0 hunks)internal/integration/subdag_test.go(3 hunks)
💤 Files with no reviewable changes (1)
- internal/integration/localdag_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Backend entrypoint incmd/orchestrates the scheduler and CLI; runtime, persistence, and service layers sit underinternal/*(for exampleinternal/runtime,internal/persistence)
Keep Go filesgofmt/goimportsclean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, andErr...names for package-level errors
Repository linting relies ongolangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers ininternal/common
Files:
internal/integration/subdag_test.go
**/*_test.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*_test.go: Co-locate Go tests as*_test.go; favour table-driven cases and cover failure paths
Usestretchr/testify/requireand shared fixtures frominternal/testinstead of duplicating mocks
Files:
internal/integration/subdag_test.go
🧠 Learnings (2)
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: Applies to **/*_test.go : Co-locate Go tests as `*_test.go`; favour table-driven cases and cover failure paths
Applied to files:
internal/integration/subdag_test.go
📚 Learning: 2025-12-04T10:34:17.062Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.062Z
Learning: `make test` (or `make test-coverage`) executes the Go suite via `gotestsum`; append `TEST_TARGET=./internal/...` to focus packages
Applied to files:
internal/integration/subdag_test.go
🧬 Code graph analysis (1)
internal/integration/subdag_test.go (4)
internal/test/helper.go (1)
Setup(81-225)internal/core/status.go (6)
Succeeded(11-11)NodeSucceeded(57-57)NodeSkipped(58-58)Failed(9-9)NodeFailed(55-55)PartiallySucceeded(13-13)internal/core/step.go (1)
Step(13-77)internal/core/execution/dagrun.go (3)
NewDAGRunRef(168-173)DAGRunStore(23-48)DAGRunAttempt(131-153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test on ubuntu-latest
- GitHub Check: Build
🔇 Additional comments (3)
internal/integration/subdag_test.go (3)
17-453: Good test coverage with well-structured subtests.The
TestInlineSubDAGfunction provides comprehensive coverage for multi-level nested local DAGs including:
- Simple execution with output capture
- 2-level and 3-level nesting scenarios
- Parallel execution
- Conditional/precondition-based execution
- Output passing between DAGs
- Failure path for non-existent references
- Partial success scenarios
The use of
t.Runsubtests andrequireassertions aligns well with the coding guidelines.
455-678: Thorough external sub-DAG and retry tests.The
TestExternalSubDAGtests provide good coverage for:
- Basic output capture from external sub-DAGs
- Retry functionality across nested sub-DAG hierarchies with status manipulation
- Retry policy with output capture verification
The retry simulation using counter files and manual status updates effectively tests the retry flow.
681-769: LGTM!The
TestRetryPolicytests properly cover both the retry success path with output capture and the baseline case without retry. Good use of counter files for simulating retry behavior.
|
We need to take care of those cases where the multiple dags defined within a single dag does not contain a dag whose name matches with another dag yaml or a dag defined in another file containing multiple dags. Orvis it that these multiple subdags defined within a single subdag are qualified by the dagyaml name to avoid name conflict (like namespaces in c++ and packages in java). If it is not so, either we should do that or we should a avoid multiple dag definitions within a single dag . I hope there is no confusion. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1493 +/- ##
==========================================
- Coverage 59.93% 59.91% -0.02%
==========================================
Files 196 196
Lines 22001 22013 +12
==========================================
+ Hits 13187 13190 +3
- Misses 7402 7408 +6
- Partials 1412 1415 +3
... and 2 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
* **New Features** * Optional parentSubDAGRunId query to fetch nested sub-DAG runs; sub-run lists and modal show root/parent DAG context, status filters with counts, and improved keyboard navigation. * **Bug Fixes / UX** * Consolidated local DAG handling for multi-level execution, improved error responses for missing runs, and small UI spacing tidy-ups. * **Tests** * Reorganized and expanded integration/unit tests for multi-level sub-DAG scenarios and temp-DAG file handling.
Summary by CodeRabbit
New Features
Bug Fixes / UX
Tests
✏️ Tip: You can customize this high-level summary in your review settings.