Skip to content

refactor(core/spec): restructure spec types and build logic#1499

Merged
yottahmd merged 24 commits intomainfrom
refactor-core
Dec 22, 2025
Merged

refactor(core/spec): restructure spec types and build logic#1499
yottahmd merged 24 commits intomainfrom
refactor-core

Conversation

@yottahmd
Copy link
Copy Markdown
Collaborator

@yottahmd yottahmd commented Dec 21, 2025

Summary by CodeRabbit

  • Refactor

    • Simplified manifest/DAG build flow into a smaller, type-driven pipeline and reduced public build surface; improved step dependency handling and defaults.
    • Adjusted DAG name validation behavior.
  • New Features

    • Added typed YAML parsers (shell, env, schedule, ports, continue-on, string-or-array, parallel) and improved multi-document/base-manifest decoding.
    • Load .env values earlier during runtime initialization to affect secret resolution.
  • Tests

    • Vastly expanded unit and integration tests for YAML loading, step building, params, schema resolution, secrets, parallelism, handlers, and loader options.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 21, 2025

Walkthrough

Replaces the old YAML definition + builder registry with a manifest-based spec and typed YAML union types; removes many legacy builders/helpers/tests; adds typed parsing types, per-manifest build methods, a decode hook flow, and relocates DAG name validation to validator.go.

Changes

Cohort / File(s) Summary
DAG name validation
internal/core/names.go, internal/core/validator.go
Removed legacy name utilities from internal/core/names.go; added/relocated DAG name validation primitives (DAGNameMaxLen, regex, ValidateDAGName) into internal/core/validator.go; removed old error identifiers.
Typed-union types (new package)
internal/core/spec/types/*
internal/core/spec/types/doc.go, internal/core/spec/types/continueon.go, internal/core/spec/types/env.go, internal/core/spec/types/port.go, internal/core/spec/types/schedule.go, internal/core/spec/types/shell.go, internal/core/spec/types/stringarray.go, internal/core/spec/types/*_test.go
Added types package with YAML-unmarshalable typed union values: ContinueOnValue, EnvValue/EnvEntry, PortValue, ScheduleValue, ShellValue, StringOrArray (and MailToValue/TagsValue aliases). Implemented parsing, validation, accessors, and extensive unit tests.
Manifest-based spec & builders
internal/core/spec/dag.go, internal/core/spec/step.go
Introduced dag and step manifest types and per-manifest build(ctx) methods that transform manifests into core.DAG/core.Step; added ordered transformer pipelines for metadata and full build phases (env, schedule, shell, secrets, steps, preconditions, retries/repeats, parallelism, sub-DAGs) with aggregated error handling.
Loader & decode hooks
internal/core/spec/loader.go, internal/core/spec/variables.go
Switched loader internals from definition to dag manifests; added TypedUnionDecodeHook and decodeViaYAML to decode YAML into typed unions; added helper loadVariablesFromEnvValue to process types.EnvValue. Public loader APIs unchanged.
Builder simplification & tests
internal/core/spec/builder.go, internal/core/spec/builder_test.go
Removed the registry-based builder system and many buildXxx helpers; retained a streamlined step-construction path concentrating on preconditions, secret refs, and step name generation; expanded/relocated unit tests for step and spec behaviors.
Removed legacy schema & helpers
internal/core/spec/definition.go, internal/core/spec/command.go, internal/core/spec/params.go, internal/core/spec/schedule.go, internal/core/spec/builder_timeout_test.go
Deleted the old definition schema and numerous helper functions (e.g., buildCommand, buildParams, buildStepParams, parseScheduleMap) and removed an old timeout test file.
Schema resolution
internal/core/spec/schema.go, internal/core/spec/schema_test.go
Added JSON Schema resolution/loading helpers (URL/file resolution, workingDir/DAG precedence, remote fetch, jsonschema resolution with ValidateDefaults) and tests validating schema fetching, resolution, and integration with params.
Spec parsing & step details
internal/core/spec/step.go, internal/core/spec/*_test.go
Added comprehensive step-level parsing and builders (command/shell parsing, params field parsing, retry/repeat policies, parallelism, sub-DAG invocation, executor inference) and comprehensive tests covering positive, edge, and error scenarios.
Typed value tests & variables
internal/core/spec/variables.go, internal/core/spec/variables_test.go, internal/core/spec/params_test.go
Added helper to load variables from types.EnvValue and many tests for variable/param parsing, evaluation, env behavior, and param override/evaluation semantics.
Integration test tweak
internal/integration/gha_test.go
Added an environment guard to skip a GitHub Actions–specific test when not running in GH Actions.
Miscellaneous runtime change
internal/runtime/agent/agent.go
Added call to a.dag.LoadDotEnv(ctx) during agent startup before secret resolution to ensure .env is applied prior to secret handling.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as LoadYAML (public API)
    participant Decoder as TypedUnionDecodeHook / decodeViaYAML
    participant Manifest as dag (manifest)
    participant Builder as dag.build()
    participant Core as core.DAG / core.Step

    Client->>Decoder: Read & decode YAML (single or multi-doc)
    Decoder->>Manifest: Unmarshal into typed unions (ShellValue, EnvValue, ScheduleValue...)
    Client->>Manifest: supply base manifest (optional)
    Manifest->>Builder: .build(ctx)
    Builder->>Builder: run metadata transformers
    Builder->>Builder: run full transformers (env, schedule, shell, secrets, steps...)
    Builder->>Core: construct core.DAG and core.Step objects
    Builder-->>Client: return core.DAG or aggregated BuildErrors
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

  • Areas needing extra attention:
    • internal/core/spec/dag.go — transformers ordering, metadata vs full build modes, BuildFlagAllowBuildErrors handling.
    • internal/core/spec/step.go — command/shell parsing, retry/repeat/backoff semantics, parallel/sub-DAG handling, executor inference and compatibility.
    • internal/core/spec/loader.go — TypedUnionDecodeHook, decodeViaYAML, multi-document/base-manifest merging.
    • internal/core/spec/types/* — YAML unmarshalling edge cases, ordering, and error messages.
    • Cross-cutting: ensure no remaining references to removed types/helpers (definition, buildCommand, buildParams, parseScheduleMap) and verify agent change loading .env before secret resolution.

Possibly related PRs

Poem

🐇 I nibbled old YAML, found typed sprout light,
Manifests stitched neat by moon and byte.
Steps now hop tidy, secrets snug in rows,
Decode hooks whisper, and the build pipeline grows.
— a rabbit, cheerfully testing at night

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.90% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'refactor(core/spec): restructure spec types and build logic' accurately reflects the main changes, which involve significant restructuring of the spec package including new type definitions, removal of old builder patterns, and refactoring of the build logic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-core

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

@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 (12)
internal/integration/gha_test.go (1)

12-14: Consider a more descriptive skip message.

The current skip message mentions "flaky test locally," but a more specific message could better explain the requirement: "Skipping: GitHub Actions executor requires GitHub Actions environment".

🔎 Suggested improvement
 	if os.Getenv("GITHUB_ACTIONS") != "true" {
-		t.Skip("Skipping flaky test locally")
+		t.Skip("Skipping: GitHub Actions executor requires GitHub Actions environment")
 	}
internal/core/spec/builder_test.go (1)

4368-4425: LGTM! Good test coverage for timeout handling.

The test suite covers the key scenarios:

  • Positive timeout correctly converts to duration
  • Explicit zero and omitted timeout both yield zero duration
  • Negative timeout properly triggers validation error

The test structure aligns with existing patterns in this file. Consider adding t.Parallel() to each subtest for consistency with tests like TestBuildStep that parallelize subtests, though this is optional given the existing mixed usage in the file.

internal/core/spec/variables.go (1)

93-128: The function is correct but duplicates core logic from loadVariables.

The evaluation/setenv loop (lines 103-125) is nearly identical to the loop in loadVariables (lines 66-88). Consider extracting the common logic into a shared helper to reduce duplication:

🔎 Potential refactor to reduce duplication
// processEnvPairs handles the common evaluation and setenv logic
func processEnvPairs(ctx BuildContext, pairs []pair) (map[string]string, error) {
    vars := map[string]string{}
    for _, p := range pairs {
        value := p.val
        if !ctx.opts.Has(BuildFlagNoEval) {
            var err error
            value, err = cmdutil.EvalString(ctx.ctx, value, cmdutil.WithVariables(vars))
            if err != nil {
                return nil, core.NewValidationError("env", p.val, fmt.Errorf("%w: %s", ErrInvalidEnvValue, p.val))
            }
            if err := os.Setenv(p.key, value); err != nil {
                return nil, core.NewValidationError("env", p.key, fmt.Errorf("%w: %s", err, p.key))
            }
        }
        vars[p.key] = value
    }
    return vars, nil
}

Then loadVariablesFromEnvValue could convert entries to pairs and call this helper.

internal/core/spec/types/port_test.go (1)

1-117: LGTM! Comprehensive test coverage for PortValue unmarshaling.

The tests cover key scenarios:

  • Integer and string representations
  • Edge cases (large port 65535, zero/unset value)
  • Error paths for invalid types (array, map)
  • Integration with struct unmarshaling

Consider adding tests for boundary cases if port validation is intended at this layer:

  • Port 0 (valid for dynamic port assignment)
  • Port > 65535 (invalid but may be caught elsewhere)

This is optional since validation might be handled at a different layer.

internal/core/spec/types/shell.go (1)

44-60: Consider validating array element types.

The implementation uses fmt.Sprintf("%v", ...) as a fallback when array elements are not strings (lines 50, 56). While this provides compatibility, it silently accepts invalid inputs like numbers or bools in shell command arrays, which could lead to unexpected behavior.

Consider either:

  1. Returning an error for non-string array elements
  2. Adding a validation layer to flag this as a warning
🔎 Alternative implementation with strict type checking
 	case []any:
 		// Array value: ["bash", "-e"]
 		if len(v) > 0 {
 			if cmd, ok := v[0].(string); ok {
 				s.command = cmd
 			} else {
-				s.command = fmt.Sprintf("%v", v[0])
+				return fmt.Errorf("shell command must be string, got %T", v[0])
 			}
 			for i := 1; i < len(v); i++ {
 				if arg, ok := v[i].(string); ok {
 					s.arguments = append(s.arguments, arg)
 				} else {
-					s.arguments = append(s.arguments, fmt.Sprintf("%v", v[i]))
+					return fmt.Errorf("shell argument at index %d must be string, got %T", i, v[i])
 				}
 			}
 		}
internal/core/spec/loader.go (1)

554-584: Consider a registry-based approach for type dispatch.

The current implementation is clear and explicit, but adding new typed union types requires modifying this function. A map-based registry could make it more extensible.

That said, the current approach has the benefit of compile-time type safety and explicit documentation of supported types.

🔎 Optional: Registry-based alternative
var typedUnionTypes = map[reflect.Type]bool{
	reflect.TypeOf(types.ShellValue{}):     true,
	reflect.TypeOf(types.StringOrArray{}):  true,
	reflect.TypeOf(types.ScheduleValue{}):  true,
	reflect.TypeOf(types.EnvValue{}):       true,
	reflect.TypeOf(types.ContinueOnValue{}): true,
	reflect.TypeOf(types.PortValue{}):      true,
}

func TypedUnionDecodeHook() mapstructure.DecodeHookFunc {
	return func(_ reflect.Type, to reflect.Type, data any) (any, error) {
		if typedUnionTypes[to] {
			return decodeViaYAMLReflect(to, data)
		}
		return data, nil
	}
}
internal/core/spec/dag.go (1)

662-780: Consider extracting common auth config parsing logic.

The map[string]any (lines 680-713) and map[any]any (lines 715-773) cases have nearly identical nested logic for parsing auth configuration. Extracting a helper function would reduce duplication and improve maintainability.

🔎 Suggested refactor to reduce duplication
+// parseAuthConfig extracts auth configuration from the given map.
+func parseAuthConfig(auth map[string]any, noEval bool) *core.AuthConfig {
+	config := &core.AuthConfig{}
+	if username, ok := auth["username"].(string); ok {
+		config.Username = username
+		if !noEval {
+			config.Username = os.ExpandEnv(config.Username)
+		}
+	}
+	if password, ok := auth["password"].(string); ok {
+		config.Password = password
+		if !noEval {
+			config.Password = os.ExpandEnv(config.Password)
+		}
+	}
+	if authStr, ok := auth["auth"].(string); ok {
+		config.Auth = authStr
+		if !noEval {
+			config.Auth = os.ExpandEnv(config.Auth)
+		}
+	}
+	return config
+}

Then use it in both map[string]any and map[any]any cases by converting the map appropriately.

internal/core/spec/step.go (4)

162-172: Consider adding a deprecation warning for Dir field.

Unlike the Run field (which logs a deprecation warning in buildSubDAG), the deprecated Dir field is silently accepted. For consistency, consider logging a warning when Dir is used.

🔎 Proposed fix
 func (s *step) buildWorkingDir(_ StepBuildContext, result *core.Step) error {
 	switch {
 	case s.WorkingDir != "":
 		result.Dir = strings.TrimSpace(s.WorkingDir)
 	case s.Dir != "":
+		// TODO: Log deprecation warning similar to Run field
 		result.Dir = strings.TrimSpace(s.Dir)
 	default:
 		result.Dir = ""
 	}
 	return nil
 }

373-395: Consider extracting common backoff parsing logic.

The backoff parsing and validation logic (lines 373-395) is nearly identical to the one in buildRetryPolicy (lines 276-297). Consider extracting this into a helper function to reduce duplication.


506-518: Minor inconsistency in CmdWithArgs construction.

In the array case, CmdWithArgs (line 517) is constructed by quoting each arg, but the cmd itself is not quoted. If cmd contains spaces (unlikely but possible), this could produce an inconsistent representation. The string case (line 480) uses the original value directly.

This is minor since CmdWithArgs appears to be primarily for display/logging purposes.


630-662: Unknown keys in parallel configuration are silently ignored.

When parallel is an object, only items and maxConcurrent are handled. Other keys (e.g., typos like maxConcurent) are silently ignored, which could lead to user confusion.

🔎 Proposed fix to warn on unknown keys
 	case map[string]any:
 		// Object configuration
 		for key, val := range v {
 			switch key {
 			case "items":
 				// ... existing code ...
 
 			case "maxConcurrent":
 				// ... existing code ...
+
+			default:
+				return core.NewValidationError("parallel", key, fmt.Errorf("unknown key %q in parallel configuration", key))
 			}
 		}
internal/core/spec/builder.go (1)

220-234: Unused parameter ctx.

The ctx BuildContext parameter is declared but not used in the function body.

🔎 Proposed fix
-func normalizeStepData(ctx BuildContext, data []any) []any {
+func normalizeStepData(_ BuildContext, data []any) []any {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2b3d8f and 1f55a1e.

📒 Files selected for processing (27)
  • internal/core/names.go (0 hunks)
  • internal/core/spec/builder.go (4 hunks)
  • internal/core/spec/builder_test.go (1 hunks)
  • internal/core/spec/builder_timeout_test.go (0 hunks)
  • internal/core/spec/command.go (0 hunks)
  • internal/core/spec/dag.go (1 hunks)
  • internal/core/spec/definition.go (0 hunks)
  • internal/core/spec/loader.go (10 hunks)
  • internal/core/spec/params.go (0 hunks)
  • internal/core/spec/schedule.go (0 hunks)
  • internal/core/spec/step.go (1 hunks)
  • internal/core/spec/types/continueon.go (1 hunks)
  • internal/core/spec/types/continueon_test.go (1 hunks)
  • internal/core/spec/types/doc.go (1 hunks)
  • internal/core/spec/types/env.go (1 hunks)
  • internal/core/spec/types/env_test.go (1 hunks)
  • internal/core/spec/types/port.go (1 hunks)
  • internal/core/spec/types/port_test.go (1 hunks)
  • internal/core/spec/types/schedule.go (1 hunks)
  • internal/core/spec/types/schedule_test.go (1 hunks)
  • internal/core/spec/types/shell.go (1 hunks)
  • internal/core/spec/types/shell_test.go (1 hunks)
  • internal/core/spec/types/stringarray.go (1 hunks)
  • internal/core/spec/types/stringarray_test.go (1 hunks)
  • internal/core/spec/variables.go (2 hunks)
  • internal/core/validator.go (1 hunks)
  • internal/integration/gha_test.go (1 hunks)
💤 Files with no reviewable changes (6)
  • internal/core/spec/builder_timeout_test.go
  • internal/core/spec/schedule.go
  • internal/core/spec/command.go
  • internal/core/names.go
  • internal/core/spec/definition.go
  • internal/core/spec/params.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/core/spec/types/stringarray_test.go
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/shell.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/builder_test.go
  • internal/core/spec/types/env.go
  • internal/core/spec/types/doc.go
  • internal/integration/gha_test.go
  • internal/core/validator.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/variables.go
  • internal/core/spec/dag.go
  • internal/core/spec/types/schedule.go
  • internal/core/spec/types/stringarray.go
  • internal/core/spec/types/port.go
  • internal/core/spec/types/continueon.go
  • internal/core/spec/loader.go
  • internal/core/spec/step.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/builder.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
Use stretchr/testify/require and shared fixtures from internal/test instead of duplicating mocks

Files:

  • internal/core/spec/types/stringarray_test.go
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/builder_test.go
  • internal/integration/gha_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/types/env_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/core/spec/types/stringarray_test.go
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/integration/gha_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/types/env_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 **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/core/spec/types/continueon_test.go
  • internal/integration/gha_test.go
  • internal/core/spec/types/shell_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/core/spec/builder_test.go
  • internal/core/spec/types/shell_test.go
📚 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/**/*.{yaml,yml} : Dagu YAML configurations should support minimal setup with sensible defaults and support for environment variable expansion and command substitution

Applied to files:

  • internal/core/spec/types/env.go
🧬 Code graph analysis (11)
internal/core/spec/types/stringarray_test.go (1)
internal/core/spec/types/stringarray.go (3)
  • StringOrArray (18-22)
  • MailToValue (83-83)
  • TagsValue (90-90)
internal/core/spec/types/port_test.go (1)
internal/core/spec/types/port.go (1)
  • PortValue (16-20)
internal/core/spec/types/continueon_test.go (2)
internal/core/spec/types/continueon.go (1)
  • ContinueOnValue (25-33)
internal/core/status.go (1)
  • Failed (9-9)
internal/core/spec/types/schedule_test.go (1)
internal/core/spec/types/schedule.go (1)
  • ScheduleValue (22-28)
internal/core/spec/builder_test.go (1)
internal/core/spec/loader.go (1)
  • LoadYAML (174-188)
internal/core/spec/types/shell_test.go (2)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/runtime/builtin/command/shell.go (1)
  • Shell (13-20)
internal/core/spec/dag.go (11)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/spec/types/stringarray.go (1)
  • StringOrArray (18-22)
internal/core/spec/types/schedule.go (1)
  • ScheduleValue (22-28)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/dag.go (12)
  • MailOn (485-488)
  • TypeChain (27-27)
  • TypeGraph (25-25)
  • TypeAgent (29-29)
  • AuthConfig (392-400)
  • HandlerOnInit (520-520)
  • HandlerOnExit (524-524)
  • HandlerOnSuccess (521-521)
  • HandlerOnFailure (522-522)
  • HandlerOnCancel (523-523)
  • OTelConfig (507-514)
  • MailConfig (499-504)
internal/core/spec/types/port.go (1)
  • PortValue (16-20)
internal/core/spec/builder.go (5)
  • BuildContext (13-22)
  • BuildFlagOnlyMetadata (49-49)
  • BuildFlagAllowBuildErrors (50-50)
  • BuildFlagSkipSchemaValidation (51-51)
  • BuildFlagNoEval (48-48)
internal/common/cmdutil/cmdutil.go (2)
  • GetShellCommand (35-61)
  • SplitCommand (142-169)
internal/common/fileutil/fileutil.go (1)
  • ResolvePathOrBlank (187-194)
internal/core/spec/errors.go (2)
  • ErrInvalidStepData (14-14)
  • ErrStepsMustBeArrayOrMap (15-15)
internal/core/spec/loader.go (1)
  • TypedUnionDecodeHook (556-584)
internal/core/spec/loader.go (8)
internal/core/spec/builder.go (1)
  • BuildContext (13-22)
internal/core/dag.go (1)
  • DAG (33-135)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/spec/types/stringarray.go (1)
  • StringOrArray (18-22)
internal/core/spec/types/schedule.go (1)
  • ScheduleValue (22-28)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/types/continueon.go (1)
  • ContinueOnValue (25-33)
internal/core/spec/types/port.go (1)
  • PortValue (16-20)
internal/core/spec/step.go (8)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/spec/types/stringarray.go (1)
  • StringOrArray (18-22)
internal/core/step.go (2)
  • ContinueOn (226-232)
  • ExecutorConfig (107-115)
internal/core/params.go (2)
  • Params (36-45)
  • NewSimpleParams (48-50)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/builder.go (2)
  • StepBuildContext (25-28)
  • BuildContext (13-22)
internal/common/cmdutil/cmdutil.go (2)
  • SplitCommand (142-169)
  • JoinCommandArgs (20-22)
internal/core/spec/errors.go (8)
  • ErrTimeoutSecMustBeNonNegative (31-31)
  • ErrInvalidSignal (21-21)
  • ErrStepCommandIsEmpty (29-29)
  • ErrStepCommandMustBeArrayOrString (30-30)
  • ErrExecutorTypeMustBeString (23-23)
  • ErrExecutorConfigValueMustBeMap (24-24)
  • ErrExecutorHasInvalidKey (25-25)
  • ErrExecutorConfigMustBeStringOrMap (26-26)
internal/core/spec/types/env_test.go (1)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/builder.go (2)
internal/core/dag.go (1)
  • SecretRef (139-148)
internal/core/spec/loader.go (1)
  • TypedUnionDecodeHook (556-584)
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest
🔇 Additional comments (65)
internal/integration/gha_test.go (1)

4-4: LGTM! Appropriate environment guard for CI-specific test.

The addition of the environment check correctly ensures this GitHub Actions-specific test only runs in the appropriate CI environment. The placement before t.Parallel() follows Go testing best practices.

Also applies to: 12-14

internal/core/validator.go (1)

9-29: LGTM!

The DAG name validation logic is well-structured:

  • Regex compiled once at package init is idiomatic Go
  • Empty names correctly allowed (caller provides via context/filename)
  • Length and character validation are properly sequenced
  • The regex pattern correctly enforces alphanumeric, underscore, dash, and dot characters
internal/core/spec/types/doc.go (1)

1-15: LGTM!

Clear and well-structured package documentation that explains the purpose and design principles of the typed union types. The four design principles provide good guidance for implementing and using types in this package.

internal/core/spec/types/schedule_test.go (2)

12-119: LGTM! Comprehensive test coverage for ScheduleValue.

The test suite thoroughly exercises all YAML unmarshalling scenarios including simple strings, arrays, maps with various keys, error conditions, and zero-value behavior. The tests properly validate accessor methods and error messages.


121-160: Good integration testing within structs.

The nested struct tests verify that ScheduleValue integrates correctly with YAML unmarshalling when used as a struct field, covering both simple and complex schedule forms.

internal/core/spec/types/continueon_test.go (2)

12-166: LGTM! Thorough test coverage for ContinueOnValue.

The test suite comprehensively covers all unmarshalling scenarios including string forms (with case insensitivity and whitespace handling), map forms with various fields, error conditions, and zero-value behavior. All accessor methods are properly validated.


168-206: Good struct integration tests.

The tests verify proper integration of ContinueOnValue within structs, covering both string and map forms, as well as the not-set scenario.

internal/core/spec/types/stringarray_test.go (3)

12-73: LGTM! Comprehensive StringOrArray unmarshalling tests.

The test suite covers all key scenarios including single strings, arrays, empty cases, and proper distinction between IsZero (not set) and IsEmpty (set but empty). The preservation of empty strings for validation layer handling is intentional and well-documented.


75-124: Good struct integration and edge case coverage.

The tests properly verify StringOrArray behavior within structs and correctly distinguish between not-set vs. explicitly-set-to-empty-array scenarios, which is important for dependency handling.


126-158: Sufficient coverage for type aliases.

The minimal tests for MailToValue and TagsValue are appropriate since they are type aliases and inherit all behavior from StringOrArray.

internal/core/spec/types/shell_test.go (2)

12-90: LGTM! Thorough ShellValue unmarshalling tests.

The test suite comprehensively covers all unmarshalling scenarios including string forms (with and without inline args), array forms, zero-value behavior, and IsArray distinction. The preservation of environment variable syntax is correctly tested.


92-118: Good struct integration tests.

The tests properly verify ShellValue behavior when embedded in structs, covering both set and not-set scenarios.

internal/core/spec/types/shell.go (1)

1-99: Implementation is functionally correct otherwise.

The ShellValue implementation properly handles all expected YAML input forms (string, array, nil) and provides appropriate accessors. The preservation of environment variable syntax and the IsArray flag are well-implemented.

internal/core/spec/types/env_test.go (2)

12-166: LGTM! Comprehensive EnvValue unmarshalling tests.

The test suite thoroughly covers all unmarshalling scenarios including map forms, array forms (with ordering preserved), string forms with KEY=value syntax, mixed forms, numeric value stringification, and values containing equals signs. The preservation of environment variable references for later expansion is correctly tested.


168-212: Good struct integration tests.

The tests properly verify EnvValue behavior within structs for both map and array forms, as well as the not-set scenario.

internal/core/spec/types/port.go (1)

1-65: Implementation is otherwise correct.

The PortValue implementation properly handles string and integer port values, provides appropriate accessors, and handles the zero-value case correctly.

internal/core/spec/types/stringarray.go (2)

40-50: Intentional stringification for numeric compatibility.

The implementation stringifies non-string array elements (line 47), which is intentional to support numeric tags and maintain compatibility. The comment clearly documents this behavior.


1-90: LGTM! Clean StringOrArray implementation.

The implementation properly handles all expected YAML input forms, provides appropriate accessors to distinguish between zero-value and empty-but-set, and includes well-documented type aliases for domain-specific uses (MailToValue, TagsValue).

internal/core/spec/types/schedule.go (4)

1-28: Well-structured type definition with clear documentation.

The ScheduleValue type follows a consistent pattern with the other typed union types in this PR, with good YAML examples in the documentation.


30-76: LGTM - Comprehensive type handling in UnmarshalYAML.

The implementation correctly handles all expected YAML input forms. The []string case (lines 59-62) provides good defensive coverage for programmatic use, even though YAML typically produces []any.


78-97: Good validation with unknown key rejection.

The map parsing correctly validates keys and provides clear error messages for unknown keys.


118-137: Clean accessor implementations following consistent patterns.

Value receivers are appropriate for these read-only accessors, consistent with other types in this package.

internal/core/spec/loader.go (4)

17-17: Import added for new types package.

The import aligns with the typed union decode hook usage below.


540-552: Decode function updated to return new manifest type.

The transition from *definition to *dag aligns with the architectural refactoring. The TypedUnionDecodeHook integration enables custom YAML unmarshalling for typed union fields.


586-603: Double serialization is necessary but adds overhead.

The decodeViaYAML helper correctly enables custom UnmarshalYAML methods to be invoked. The double serialization (data → YAML bytes → target type) is the cost of bridging mapstructure with go-yaml's custom unmarshalers.

For performance-critical scenarios, consider caching or alternative approaches, but this is acceptable for configuration loading which happens infrequently.


348-380: Multi-document loading flow updated correctly.

The transition to manifest-based decoding and method-based building is consistent throughout the function. Error handling and context management are preserved.

internal/core/spec/types/env.go (4)

29-39: Clear type definitions for environment variable handling.

The EnvEntry struct provides a clean representation for key-value pairs, and EnvValue follows the established pattern for typed union types.


77-96: Array parsing handles mixed formats correctly.

The function supports both map and string entries in arrays, with clear error messages including the index position.


98-105: Simple and effective value stringification.

Using %v format is appropriate for converting various YAML types to environment variable string values.


69-75: Map iteration order in parseMap is non-deterministic; users requiring ordered definitions should use the array syntax.

The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. The parseMap function iterates directly over the map argument, which means environment variable ordering cannot be guaranteed when using map form in YAML. This is acceptable since the documentation explicitly recommends the array form for ordered definitions, which is preserved through the array structure while supporting both map and string formats.

internal/core/spec/dag.go (6)

18-105: Comprehensive intermediate representation mirroring YAML structure.

The dag struct and associated helper types provide a clean intermediate representation that maps directly to the YAML schema. The use of typed union types (types.ShellValue, types.EnvValue, etc.) enables flexible YAML parsing.


214-306: Well-structured build method with transformer pattern.

The transformer pattern provides clean separation of concerns and consistent error handling. The meta flag for each transformer correctly distinguishes metadata-only fields from full build requirements.

The error aggregation with BuildFlagAllowBuildErrors support allows graceful degradation when loading DAGs with errors.


402-419: Environment variable builder correctly populates build context.

The method stores env vars in ctx.buildEnv for cross-transformer communication (e.g., allowing params to reference env vars like P2=${A001}). Since BuildContext contains a map field, modifications are visible to subsequent transformers within the same build call.


988-1071: Comprehensive step building with support for multiple formats.

The method correctly handles:

  • Array of steps with chain dependency injection
  • Nested arrays for parallel step groups
  • Map-based step definitions with names as keys

The use of TypedUnionDecodeHook for map decoding (line 1051) ensures consistent handling of typed union fields within steps.


325-341: Type validation with clear defaults and future-proofing.

Defaulting to TypeChain when type is empty, and reserving TypeAgent for future use with a clear error message, provides good forward compatibility.


566-603: Robust working directory resolution with sensible fallbacks.

The cascade of fallbacks (explicit → default → file directory → current directory → home directory) provides resilient behavior. The fallback to UserHomeDir when Getwd fails is reasonable for edge cases.

internal/core/spec/types/continueon.go (5)

25-33: Clear type definition for continue-on configuration.

The ContinueOnValue struct captures all supported continuation conditions with a consistent pattern matching other typed union types.


35-69: Good case-insensitive string handling.

Using strings.ToLower(strings.TrimSpace(v)) for the string shorthand allows flexible input like "Skipped", "FAILED", etc.


71-109: Good backward compatibility with "failure" alias.

Supporting both "failed" and "failure" (line 80) provides flexibility for users. Unknown keys are properly rejected with clear error messages.


135-186: Integer parsing handles various YAML numeric representations.

The functions correctly handle Go's type variations from YAML parsing (int, int64, float64, uint64). The nolint:gosec comments appropriately acknowledge the uint64 overflow risk, which is acceptable since exit codes are small numbers (typically 0-255).

Note: The float64 to int conversion truncates rather than rounds, which is the expected behavior for exit codes parsed from YAML.


188-207: Clean accessor implementations.

The accessors follow the established pattern with value receivers for read-only access.

internal/core/spec/step.go (17)

1-14: LGTM!

Imports are well-organized and all appear to be used throughout the file.


16-85: LGTM!

The step struct is comprehensive with good documentation. The deprecation notices for Dir and Run are clear.


87-106: LGTM!

Using any type for fields that accept multiple input types (bool/string/number) is appropriate for flexible YAML parsing, with proper type validation in the corresponding build methods.


108-160: LGTM!

The transformer pipeline pattern is clean and maintainable. Error aggregation provides good user experience by reporting all validation issues at once. The comment on line 138 explaining the ordering constraint is helpful.


174-198: LGTM!

Shell parsing correctly handles both array and string forms, with proper error context on parse failures.


200-209: LGTM!

Timeout validation and duration conversion are correct.


211-220: LGTM!

The distinction between unset and explicitly empty dependencies is correctly handled, which is important for chain-type DAG behavior.


222-234: LGTM!

Clean mapping from the typed ContinueOnValue to core.ContinueOn.


236-305: LGTM!

The retry policy parsing handles various YAML input types correctly. The backoff validation ensuring either 0 (disabled) or >1.0 (exponential growth) is a good constraint.


405-415: LGTM!

Signal validation is correct.


417-429: LGTM!

Output field parsing correctly handles both variable ($VAR) and direct forms.


431-439: LGTM!

Environment variable mapping is straightforward and correct.


441-454: LGTM!

Correctly handles both precondition and preconditions fields, merging them together.


527-546: LGTM!

Parameter parsing and conversion to core.SimpleParams is clean.


548-605: LGTM!

Executor parsing correctly handles string, map, and nil forms with appropriate inference from DAG context.


671-723: LGTM!

Sub-DAG handling correctly manages the deprecated Run field with warnings, parameter serialization, and executor type selection based on parallel configuration.


725-767: LGTM!

Parallel items parsing handles various value types appropriately, and using DeterministicMap ensures consistent ordering for reproducible builds.

internal/core/spec/builder.go (7)

12-78: LGTM!

BuildContext, StepBuildContext, BuildFlag, and BuildOpts are well-structured with clear documentation.


80-148: LGTM!

Precondition parsing is comprehensive, handling all expected input forms with proper validation.


186-217: LGTM!

Type-based step name generation provides meaningful auto-generated names with uniqueness guarantee.


236-255: LGTM!

The refactored buildStepFromRaw cleanly integrates the new step type with TypedUnionDecodeHook for proper unmarshaling of custom types.


257-294: LGTM!

Chain dependency injection correctly handles the preference for ID over Name and checks both to avoid duplicate entries.


296-305: LGTM!

The alternate key lookup logic is correct and handles the ID/Name relationship properly.


150-184: LGTM!

Secret reference parsing includes proper validation for required fields and duplicate detection. Error messages include the index for easy identification. The secretRef type is properly defined in internal/core/spec/dag.go and the validation pattern is consistent with the codebase.

Comment thread internal/core/spec/types/port.go
Copy link
Copy Markdown

@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 (1)
internal/core/spec/step.go (1)

380-402: Consider extracting backoff parsing to a shared helper.

The backoff parsing logic (type switch, default multiplier for true, validation for values in (0, 1.0]) is duplicated between buildRetryPolicy (lines 283-304) and buildRepeatPolicy (lines 381-402).

🔎 Suggested helper function
// parseBackoff parses a backoff value that can be bool or numeric.
// Returns the parsed value and any validation error.
func parseBackoff(fieldName string, value any) (float64, error) {
	var backoff float64
	switch v := value.(type) {
	case bool:
		if v {
			backoff = 2.0 // Default multiplier when true
		}
	case int:
		backoff = float64(v)
	case int64:
		backoff = float64(v)
	case float64:
		backoff = v
	default:
		return 0, fmt.Errorf("invalid type for %s: %T", fieldName, v)
	}

	if backoff > 0 && backoff <= 1.0 {
		return 0, fmt.Errorf("%s must be greater than 1.0 for exponential growth", fieldName)
	}
	return backoff, nil
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f55a1e and 0a82c97.

📒 Files selected for processing (2)
  • internal/core/spec/schedule.go (0 hunks)
  • internal/core/spec/step.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/core/spec/schedule.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/core/spec/step.go
🧬 Code graph analysis (1)
internal/core/spec/step.go (8)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/spec/types/stringarray.go (1)
  • StringOrArray (18-22)
internal/core/spec/types/continueon.go (1)
  • ContinueOnValue (25-33)
internal/core/params.go (2)
  • Params (36-45)
  • NewSimpleParams (48-50)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/builder.go (2)
  • StepBuildContext (25-28)
  • BuildContext (13-22)
internal/common/cmdutil/cmdutil.go (2)
  • SplitCommand (142-169)
  • JoinCommandArgs (20-22)
internal/core/spec/errors.go (7)
  • ErrInvalidSignal (21-21)
  • ErrStepCommandIsEmpty (29-29)
  • ErrStepCommandMustBeArrayOrString (30-30)
  • ErrExecutorTypeMustBeString (23-23)
  • ErrExecutorConfigValueMustBeMap (24-24)
  • ErrExecutorHasInvalidKey (25-25)
  • ErrExecutorConfigMustBeStringOrMap (26-26)
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest
🔇 Additional comments (11)
internal/core/spec/step.go (11)

1-15: LGTM!

Package declaration and imports are well-organized, with standard library imports separated from internal packages.


17-86: LGTM!

The step struct is well-documented with clear field descriptions. The use of typed values from the types package (ShellValue, StringOrArray, etc.) for structured parsing, combined with any for flexible YAML unmarshaling where needed, is appropriate for this transformation layer.


88-107: LGTM!

The policy structs use any appropriately to support flexible YAML input types (bool, string, number), with validation deferred to the respective build methods.


109-161: LGTM!

The transformer pipeline pattern is clean and maintainable. Error aggregation via core.ErrorList provides comprehensive validation feedback. The explicit ordering comment for parallel before subDAG is helpful for future maintainers.


163-199: LGTM!

The buildWorkingDir correctly prioritizes WorkingDir over the deprecated Dir field. The buildShell method properly handles both array and string forms, using cmdutil.SplitCommand for string parsing.


201-235: LGTM!

The timeout validation, dependency handling with explicit no-deps flag, and continue-on field mapping are all correctly implemented.


237-312: LGTM!

The retry policy builder handles type flexibility well with appropriate overflow checks for uint64 conversions. The validation requiring both limit and intervalSec when retryPolicy is specified ensures complete configuration.

Note: YAML parsers typically unmarshal integers as int or float64 depending on the library. The multiple integer type cases (int, int64, uint64) provide good coverage for different parsing behaviors.


412-461: LGTM!

Signal validation, output variable handling (with or without $ prefix), environment variable formatting, and precondition merging from both singular and plural fields are all correctly implemented.


534-679: LGTM!

The builders for params, executor, and parallel are well-implemented with comprehensive type handling. The executor inference from DAG context (container/SSH) when nil is a good default behavior.


681-733: LGTM!

The sub-DAG builder correctly handles both Call and the deprecated Run field with appropriate warnings. The executor type selection based on parallel configuration ensures correct execution mode. Parameter parsing with BuildFlagNoEval prevents premature variable expansion.


735-777: LGTM!

The parseParallelItems method handles various input types robustly and uses collections.DeterministicMap to ensure consistent parameter ordering, which is important for reproducible behavior.

Comment thread internal/core/spec/step.go
Copy link
Copy Markdown

@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)
internal/core/spec/builder_test.go (1)

4399-4455: Comprehensive timeout validation tests.

The test suite covers positive, zero (explicit and omitted), and negative timeout scenarios. All validation logic is properly tested.

Minor observation: The NegativeTimeout subtest could also be marked t.Parallel() since it doesn't modify shared state or environment variables.

Optional: Add t.Parallel() to NegativeTimeout test
 	// Negative timeout should fail validation
 	t.Run("NegativeTimeout", func(t *testing.T) {
+		t.Parallel()
+
 		data := []byte(`
 steps:
internal/core/spec/types/continueon_test.go (1)

105-121: Consider adding a standalone test for markSuccess.

While markSuccess is tested here as part of the "all fields" scenario, the other boolean fields (skipped and failed) have dedicated standalone tests (lines 51-67). For consistency, consider adding a standalone test case:

t.Run("map form - markSuccess only", func(t *testing.T) {
    data := `markSuccess: true`
    var c types.ContinueOnValue
    err := yaml.Unmarshal([]byte(data), &c)
    require.NoError(t, err)
    assert.True(t, c.MarkSuccess())
    assert.False(t, c.Skipped())
    assert.False(t, c.Failed())
})

This is purely optional and improves test symmetry.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a82c97 and c9dbbe8.

📒 Files selected for processing (6)
  • internal/core/spec/builder_test.go (2 hunks)
  • internal/core/spec/loader_test.go (2 hunks)
  • internal/core/spec/types/continueon_test.go (1 hunks)
  • internal/core/spec/types/port.go (1 hunks)
  • internal/core/spec/types/port_test.go (1 hunks)
  • internal/core/spec/types/shell_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/port.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/builder_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
Use stretchr/testify/require and shared fixtures from internal/test instead of duplicating mocks

Files:

  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/builder_test.go
🧠 Learnings (4)
📓 Common learnings
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/**/*.{yaml,yml} : Dagu workflows must use YAML format with support for JSON Schema for IDE auto-completion
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/**/*.{yaml,yml} : Dagu YAML configurations should support minimal setup with sensible defaults and support for environment variable expansion and command substitution
📚 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/core/spec/types/continueon_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/builder_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 **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/shell_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/core/spec/types/continueon_test.go
🧬 Code graph analysis (3)
internal/core/spec/types/continueon_test.go (2)
internal/core/spec/types/continueon.go (1)
  • ContinueOnValue (25-33)
internal/core/step.go (1)
  • ContinueOn (226-232)
internal/core/spec/loader_test.go (3)
internal/core/spec/loader.go (9)
  • Load (150-171)
  • OnlyMetadata (72-76)
  • WithDAGsDir (90-94)
  • WithAllowBuildErrors (101-105)
  • SkipSchemaValidation (108-112)
  • WithBaseConfig (44-48)
  • WithSkipBaseHandlers (117-121)
  • WithParams (51-62)
  • WithoutEval (65-69)
internal/core/dag.go (2)
  • MailOn (485-488)
  • HandlerOn (476-482)
internal/core/execution/context.go (1)
  • WithParams (169-173)
internal/core/spec/types/shell_test.go (1)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest
🔇 Additional comments (11)
internal/core/spec/types/shell_test.go (1)

1-143: Excellent test coverage for ShellValue!

The test suite is comprehensive and well-structured:

  • Covers all input forms (string, array, inline, multiline)
  • Tests edge cases (empty, null, zero-value)
  • Validates error handling for invalid types
  • Includes integration tests with struct embedding
  • Follows Go testing best practices with testify/require and assert
internal/core/spec/loader_test.go (3)

54-77: Good expansion of metadata-only test coverage.

The test now explicitly verifies that non-metadata fields from YAML are correctly ignored in metadata-only mode, while defaults are still applied by initialization. This improves clarity and correctness validation.


630-753: Comprehensive coverage of loader options.

The new test function thoroughly validates various loader options with clean test setup and appropriate use of parallel execution. Each subtest clearly verifies the expected behavior.


755-772: Correct handling of environment manipulation in test.

Good decision to avoid t.Parallel() since this test uses t.Setenv(), which modifies the environment and would cause issues with concurrent tests. The test properly validates that environment variable expansion is disabled with the WithoutEval option.

internal/core/spec/builder_test.go (5)

1478-1507: Good validation test coverage for retry policy.

The new tests properly validate that both limit and intervalSec are required fields when retryPolicy is specified. The tests follow the established pattern and use appropriate assertions.


4457-4524: Excellent handler test coverage.

The tests comprehensively validate handler functionality including all handler types, backward compatibility with deprecated cancel, and proper conflict detection between abort and cancel. Well-structured and thorough.


4526-4807: Exceptional test coverage for parallel execution.

This comprehensive test suite covers all aspects of parallel execution configuration:

  • Multiple input forms (string, array, object)
  • Various value types (strings, numbers, objects, booleans)
  • Configuration options (maxConcurrent as int and float)
  • Error handling for invalid types and values
  • Executor type inference

The tests are well-organized and thoroughly validate the complex parallel execution feature.


4809-4856: Good coverage of registry authentication formats.

The tests validate different input formats for registry authentication (JSON strings and per-registry maps) and properly test error handling for invalid types. This complements the existing registry auth tests with additional scenarios.


4858-4915: Solid integration tests for build options.

The tests properly validate build flag behavior:

  • WithoutEval correctly avoids parallelization due to environment modification
  • WithAllowBuildErrors verifies error collection without failing the build
  • SkipSchemaValidation confirms schema validation can be bypassed

Good integration test coverage that complements the loader option tests.

internal/core/spec/types/continueon_test.go (2)

1-10: LGTM! Clean package structure and imports.

The external test package types_test appropriately tests the public API, and the imports follow the coding guidelines by using stretchr/testify/require and assert.


216-254: LGTM! Good integration testing.

The struct integration tests appropriately verify that ContinueOnValue works correctly when embedded in a configuration struct, covering string form, map form, and the unset scenario with IsZero().

Comment thread internal/core/spec/types/continueon_test.go
Copy link
Copy Markdown

@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 (4)
internal/core/spec/types/schedule_test.go (1)

162-216: Thorough edge case coverage.

This additional test suite effectively covers important edge cases including the Value() method, null/empty distinctions, and various invalid type scenarios. The tests complement the main suite well.

💡 Optional: Consider consolidating similar error cases

The error test cases (lines 194-215) test similar invalid type scenarios. If you prefer more concise test code, these could optionally be consolidated into a table-driven test with test cases defining the input and expected error substring. However, the current structure is clear and perfectly acceptable.

internal/core/spec/types/env_test.go (2)

65-76: Complete the test assertions.

The "mixed array" test only verifies the count of entries but doesn't validate the actual key-value pairs or their order. Since this test specifically checks mixed formats (maps and strings), it should verify that both formats are parsed correctly.

🔎 Suggested assertions to add
 	t.Run("mixed array - maps and strings", func(t *testing.T) {
 		data := `
 - KEY1: value1
 - KEY2=value2
 - KEY3: value3
 `
 		var e types.EnvValue
 		err := yaml.Unmarshal([]byte(data), &e)
 		require.NoError(t, err)
 		entries := e.Entries()
 		require.Len(t, entries, 3)
+		assert.Equal(t, "KEY1", entries[0].Key)
+		assert.Equal(t, "value1", entries[0].Value)
+		assert.Equal(t, "KEY2", entries[1].Key)
+		assert.Equal(t, "value2", entries[1].Value)
+		assert.Equal(t, "KEY3", entries[2].Key)
+		assert.Equal(t, "value3", entries[2].Value)
 	})

243-265: Consider table-driven tests for invalid type cases.

The three invalid type tests (lines 243-265) follow an identical pattern and could be consolidated into a single table-driven test to reduce duplication and improve maintainability.

🔎 Example table-driven approach
-	t.Run("invalid type in array - number", func(t *testing.T) {
-		data := `[123]`
-		var e types.EnvValue
-		err := yaml.Unmarshal([]byte(data), &e)
-		require.Error(t, err)
-		assert.Contains(t, err.Error(), "expected map or string")
-	})
-
-	t.Run("invalid type in array - boolean", func(t *testing.T) {
-		data := `[true]`
-		var e types.EnvValue
-		err := yaml.Unmarshal([]byte(data), &e)
-		require.Error(t, err)
-		assert.Contains(t, err.Error(), "expected map or string")
-	})
-
-	t.Run("invalid type - number", func(t *testing.T) {
-		data := `123`
-		var e types.EnvValue
-		err := yaml.Unmarshal([]byte(data), &e)
-		require.Error(t, err)
-		assert.Contains(t, err.Error(), "must be map or array")
-	})
+	tests := []struct {
+		name        string
+		data        string
+		errContains string
+	}{
+		{
+			name:        "invalid type in array - number",
+			data:        `[123]`,
+			errContains: "expected map or string",
+		},
+		{
+			name:        "invalid type in array - boolean",
+			data:        `[true]`,
+			errContains: "expected map or string",
+		},
+		{
+			name:        "invalid type - number",
+			data:        `123`,
+			errContains: "must be map or array",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var e types.EnvValue
+			err := yaml.Unmarshal([]byte(tt.data), &e)
+			require.Error(t, err)
+			assert.Contains(t, err.Error(), tt.errContains)
+		})
+	}
internal/core/spec/types/stringarray_test.go (1)

22-34: Add IsEmpty() and IsZero() assertions for consistency.

These test cases verify Values() but omit IsEmpty() and IsZero() checks. For consistency with other subtests (lines 13-20, 36-44) and to ensure the type's state methods behave correctly, consider adding these assertions.

🔎 Suggested assertions to add

For the "array of strings inline" test:

 		assert.Equal(t, []string{"step1", "step2", "step3"}, s.Values())
+		assert.False(t, s.IsEmpty())
+		assert.False(t, s.IsZero())

For the "multiline array" test:

 		assert.Equal(t, []string{"step1", "step2"}, s.Values())
+		assert.False(t, s.IsEmpty())
+		assert.False(t, s.IsZero())
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9dbbe8 and 4133900.

📒 Files selected for processing (6)
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/types/stringarray_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/core/spec/types/shell_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/port_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/core/spec/types/env_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/types/stringarray_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
Use stretchr/testify/require and shared fixtures from internal/test instead of duplicating mocks

Files:

  • internal/core/spec/types/env_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/types/stringarray_test.go
🧠 Learnings (3)
📓 Common learnings
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/**/*.{yaml,yml} : Dagu workflows must use YAML format with support for JSON Schema for IDE auto-completion
📚 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/core/spec/types/env_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/types/stringarray_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 **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/core/spec/types/env_test.go
🧬 Code graph analysis (2)
internal/core/spec/types/env_test.go (1)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/types/schedule_test.go (1)
internal/core/spec/types/schedule.go (1)
  • ScheduleValue (22-28)
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest
🔇 Additional comments (8)
internal/core/spec/types/schedule_test.go (3)

1-10: Excellent package structure and imports.

Using the types_test package for black-box testing is a best practice, and the imports are clean and appropriate.


12-119: Comprehensive test coverage for YAML unmarshalling.

This test function thoroughly covers all supported formats (single string, arrays, maps with start/stop/restart keys) and includes proper error path testing. The use of subtests with descriptive names makes the test suite highly maintainable.


121-160: Well-designed integration tests.

These tests verify that ScheduleValue correctly integrates as a struct field, which is essential for validating the real-world usage pattern in DAG configurations. The test cases appropriately cover simple, complex, and omitted schedule scenarios.

internal/core/spec/types/env_test.go (3)

1-10: LGTM!

Package declaration and imports follow Go conventions. Using types_test package for black-box testing is appropriate, and the use of testify aligns with project guidelines.


168-212: LGTM!

The struct integration tests provide solid coverage of how EnvValue behaves when embedded in a config struct. The tests correctly verify both the zero-value case and different YAML representations (map vs array).


214-242: LGTM!

These tests provide excellent coverage of the Value() method's behavior with different YAML types and the null/zero-value handling. The type assertions properly validate the raw value storage.

internal/core/spec/types/stringarray_test.go (2)

1-204: Excellent test coverage and structure.

The test suite is comprehensive and well-organized, following Go best practices with table-driven subtests. It thoroughly exercises StringOrArray unmarshaling behavior across scalars, sequences, empty values, and error cases, and validates both the base type and its aliases (MailToValue, TagsValue). The tests align with the coding guidelines for test structure and assertions.


184-203: No changes needed. The tests correctly verify the intentional stringification behavior for StringOrArray. Array elements are explicitly converted to strings for compatibility (as documented in the implementation), while bare non-string scalars are rejected. This asymmetric design is documented in code comments and the tests properly cover both the happy path and error cases.

Comment on lines +46 to +52
t.Run("empty array", func(t *testing.T) {
var s types.StringOrArray
err := yaml.Unmarshal([]byte(`[]`), &s)
require.NoError(t, err)
assert.Empty(t, s.Values())
assert.True(t, s.IsEmpty())
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add IsZero() assertion for the empty array case.

The test verifies IsEmpty() but omits IsZero(). Based on the semantics demonstrated in line 121, an explicitly set empty array should have IsZero() == false (to distinguish "not set" from "set to empty"). Add this assertion to validate the behavior.

🔎 Suggested assertion to add
 		assert.Empty(t, s.Values())
 		assert.True(t, s.IsEmpty())
+		assert.False(t, s.IsZero()) // Was explicitly set
🤖 Prompt for AI Agents
In internal/core/spec/types/stringarray_test.go around lines 46 to 52, the test
for the "empty array" case currently asserts Values() is empty and IsEmpty() is
true but omits asserting IsZero(); add an assertion that the
empty-but-explicitly-set array is not considered "zero" by calling
assert.False(t, s.IsZero()) (use the same testing/assert style as the
surrounding assertions) to validate the semantics that an explicitly set empty
array differs from an unset value.

Copy link
Copy Markdown

@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: 3

♻️ Duplicate comments (2)
internal/core/spec/types/stringarray_test.go (1)

49-55: Add IsZero() assertion for the empty array case.

The test verifies IsEmpty() but omits IsZero(). An explicitly set empty array should have IsZero() == false to distinguish "not set" from "set to empty" (as demonstrated in line 167). Add this assertion to validate the behavior.

🔎 Suggested assertion to add
 	{
 		name:         "EmptyArray",
 		input:        "[]",
 		wantValues:   nil,
 		checkIsEmpty: true,
 		wantIsEmpty:  true,
+		checkNotZero: true,
 	},
internal/core/spec/step.go (1)

578-599: String command case missing CmdArgsSys assignment.

This was flagged in a previous review. The string command parsing (lines 592-598) sets CmdWithArgs, Command, and Args but does not set CmdArgsSys, unlike the array command case (line 630). Runtime code in internal/runtime/node.go depends on CmdArgsSys being populated.

🔎 Proposed fix
 		result.CmdWithArgs = val
 		cmd, args, err := cmdutil.SplitCommand(val)
 		if err != nil {
 			return core.NewValidationError("command", val, fmt.Errorf("failed to parse command: %w", err))
 		}
 		result.Command = strings.TrimSpace(cmd)
 		result.Args = args
+		result.CmdArgsSys = cmdutil.JoinCommandArgs(result.Command, result.Args)
🧹 Nitpick comments (10)
internal/core/spec/types/continueon_test.go (2)

25-26: Consider removing unused checkIsZero field from test table.

The checkIsZero field on line 25 is defined but never set to true in any test case within this table. The zero-value behavior is tested separately in the "ZeroValue" subtest at line 209. Consider removing this unused field to reduce confusion.

🔎 Proposed fix
 	tests := []struct {
 		name            string
 		input           string
 		wantErr         bool
 		errContains     string
 		wantSkipped     bool
 		wantFailed      bool
 		wantExitCode    []int
 		wantOutput      []string
 		wantMarkSuccess bool
-		checkIsZero     bool
 		checkNotZero    bool
 	}{

And update the test runner accordingly:

 		require.NoError(t, err)
-		if tt.checkIsZero {
-			assert.True(t, c.IsZero())
-			return
-		}
 		if tt.checkNotZero {

324-327: Good addition of the "failure" alias test.

This addresses the past review request. However, consider adding two additional edge cases for complete coverage of the "failure" alias behavior:

  1. Combined failure: true with failed: true in the same map (both should result in Failed() being true)
  2. String form "failure" should be rejected (only "skipped" and "failed" are valid string values)
🔎 Suggested additional test cases
{
    name:       "FailureAndFailedCombined",
    input:      "failure: true\nfailed: true",
    wantFailed: true,
},
{
    name:        "StringFailureRejected",
    input:       "failure",
    wantErr:     true,
    errContains: "expected 'skipped' or 'failed'",
},
internal/core/spec/types/env_test.go (1)

46-53: Incomplete assertion for ArrayOfStrings test case.

The wantEntries map only verifies KEY1 but not KEY2, even though wantEntryCount is 2. This appears to be an oversight.

🔎 Proposed fix
 		{
 			name: "ArrayOfStrings",
 			input: `
 - KEY1=value1
 - KEY2=value2
 `,
 			wantEntryCount: 2,
-			wantEntries:    map[string]string{"KEY1": "value1"},
+			wantEntries:    map[string]string{"KEY1": "value1", "KEY2": "value2"},
 		},
internal/core/spec/schema_test.go (3)

565-599: Missing t.Parallel() at top-level and CWD change affects test isolation.

TestBuildParamsSchemaResolution is missing t.Parallel() at the top level (unlike other tests in this file). The subtests change the current working directory with os.Chdir, which is inherently incompatible with parallel execution. While the cleanup restores CWD, this can still cause flakiness if run alongside other tests that depend on CWD.

Consider documenting this limitation or refactoring to avoid CWD manipulation.


649-652: Silent error handling in CWD restoration.

The deferred os.Chdir(orig) discards the error with _ =. If the CWD restoration fails, subsequent tests could be affected without any indication. Consider logging the error or using t.Cleanup with error checking as done in the "FromWorkingDir" subtest.

🔎 Proposed fix
-		defer func() { _ = os.Chdir(orig) }()
+		t.Cleanup(func() {
+			if err := os.Chdir(orig); err != nil {
+				t.Errorf("failed to restore working directory: %v", err)
+			}
+		})

497-503: Consider using t.TempDir() for automatic cleanup.

Using os.CreateTemp with manual defer os.Remove is less idiomatic than t.TempDir() which handles cleanup automatically. Other tests in this file already use t.TempDir().

🔎 Proposed fix
-	tmpFile, err := os.CreateTemp("", "test-schema-*.json")
-	require.NoError(t, err)
-	defer func() { _ = os.Remove(tmpFile.Name()) }()
-
-	_, err = tmpFile.WriteString(schemaContent)
-	require.NoError(t, err)
-	require.NoError(t, tmpFile.Close())
+	tmpDir := t.TempDir()
+	schemaPath := filepath.Join(tmpDir, "schema.json")
+	require.NoError(t, os.WriteFile(schemaPath, []byte(schemaContent), 0600))

Then update the YAML template to use schemaPath instead of tmpFile.Name().

internal/core/spec/step.go (3)

298-312: Shell parsing performed twice for each step.

parseStepShellInternal is called separately for buildStepShell and buildStepShellArgs, parsing the same shell value twice. Consider caching the result or combining these into a single transformer that sets both fields.


390-412: Backoff parsing logic duplicated between retry and repeat policies.

The backoff parsing and validation (bool → 2.0 default, numeric conversion, >1.0 validation) is nearly identical in buildStepRetryPolicy and buildStepRepeatPolicy. Consider extracting a shared helper function.

🔎 Example helper
func parseBackoff(field string, value any) (float64, error) {
	switch v := value.(type) {
	case bool:
		if v {
			return 2.0, nil // Default multiplier
		}
		return 0, nil
	case int:
		return float64(v), nil
	case int64:
		return float64(v), nil
	case float64:
		return v, nil
	default:
		return 0, fmt.Errorf("invalid type for %s: %T", field, v)
	}
}

Also applies to: 489-511


771-772: float64 to int conversion truncates without warning.

When maxConcurrent is specified as a float (e.g., 5.9), it's silently truncated to 5. This is likely fine for configuration, but consider validating that the float is a whole number or documenting this behavior.

internal/core/spec/dag.go (1)

621-629: Document the limitation with parameter-derived environment variables.

The code intentionally ignores envs derived from parameters (line 629), with a comment noting "This is a limitation of the current transformer design." While this is acknowledged, it's worth clarifying:

  1. Impact: Parameters won't be automatically available as environment variables during the DAG build process
  2. Workaround: If parameter-to-env propagation is needed, it should be handled at a different layer

Consider either:

  • Adding a TODO comment with a tracking issue reference
  • Documenting this limitation in user-facing documentation
💡 Suggested improvement
 	// Note: envs from params are handled separately - they should be appended to Env
 	// This is a limitation of the current transformer design; we may need to handle this specially
+	// TODO(tracking-issue): Implement parameter-to-environment propagation in the build pipeline
 	_ = envs
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4133900 and ce71a87.

📒 Files selected for processing (17)
  • internal/core/spec/builder_test.go
  • internal/core/spec/dag.go
  • internal/core/spec/dag_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/params.go
  • internal/core/spec/params_test.go
  • internal/core/spec/schema.go
  • internal/core/spec/schema_test.go
  • internal/core/spec/step.go
  • internal/core/spec/step_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/types/port_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/types/stringarray_test.go
  • internal/core/spec/variables_test.go
💤 Files with no reviewable changes (1)
  • internal/core/spec/params.go
✅ Files skipped from review due to trivial changes (1)
  • internal/core/spec/step_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/spec/types/shell_test.go
  • internal/core/spec/types/port_test.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/core/spec/variables_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/params_test.go
  • internal/core/spec/dag_test.go
  • internal/core/spec/dag.go
  • internal/core/spec/schema.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/schema_test.go
  • internal/core/spec/types/stringarray_test.go
  • internal/core/spec/step.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
Use stretchr/testify/require and shared fixtures from internal/test instead of duplicating mocks

Files:

  • internal/core/spec/variables_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/params_test.go
  • internal/core/spec/dag_test.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/schema_test.go
  • internal/core/spec/types/stringarray_test.go
🧠 Learnings (4)
📓 Common learnings
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/**/*.{yaml,yml} : Dagu workflows must use YAML format with support for JSON Schema for IDE auto-completion
📚 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/core/spec/variables_test.go
  • internal/core/spec/types/schedule_test.go
  • internal/core/spec/loader_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/params_test.go
  • internal/core/spec/dag_test.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/schema_test.go
  • internal/core/spec/types/stringarray_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 **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/core/spec/variables_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/params_test.go
  • internal/core/spec/types/env_test.go
  • internal/core/spec/schema_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/core/spec/variables_test.go
  • internal/core/spec/types/continueon_test.go
  • internal/core/spec/params_test.go
  • internal/core/spec/dag_test.go
🧬 Code graph analysis (8)
internal/core/spec/types/schedule_test.go (1)
internal/core/spec/types/schedule.go (1)
  • ScheduleValue (22-28)
internal/core/spec/loader_test.go (1)
internal/core/spec/loader.go (10)
  • Load (150-171)
  • OnlyMetadata (72-76)
  • LoadYAMLWithOpts (191-217)
  • WithDAGsDir (90-94)
  • WithAllowBuildErrors (101-105)
  • SkipSchemaValidation (108-112)
  • WithBaseConfig (44-48)
  • WithSkipBaseHandlers (117-121)
  • WithParams (51-62)
  • WithoutEval (65-69)
internal/core/spec/types/continueon_test.go (1)
internal/core/spec/types/continueon.go (1)
  • ContinueOnValue (25-33)
internal/core/spec/dag_test.go (6)
internal/core/spec/builder.go (2)
  • BuildContext (13-22)
  • BuildOpts (56-73)
internal/core/spec/types/port.go (1)
  • PortValue (16-20)
internal/core/spec/types/stringarray.go (1)
  • StringOrArray (18-22)
internal/core/dag.go (6)
  • TypeChain (27-27)
  • TypeGraph (25-25)
  • MailOn (485-488)
  • AuthConfig (392-400)
  • OTelConfig (507-514)
  • SecretRef (139-148)
internal/core/container.go (4)
  • PullPolicyMissing (92-92)
  • PullPolicyAlways (90-90)
  • StartupCommand (54-54)
  • WaitForHealthy (62-62)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/spec/schema.go (1)
internal/common/fileutil/fileutil.go (2)
  • ResolvePath (153-183)
  • FileExists (44-47)
internal/core/spec/types/env_test.go (1)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/types/stringarray_test.go (1)
internal/core/spec/types/stringarray.go (3)
  • StringOrArray (18-22)
  • MailToValue (83-83)
  • TagsValue (90-90)
internal/core/spec/step.go (8)
internal/core/spec/types/shell.go (1)
  • ShellValue (21-26)
internal/core/step.go (2)
  • ContinueOn (226-232)
  • ExecutorConfig (107-115)
internal/core/params.go (2)
  • Params (36-45)
  • NewSimpleParams (48-50)
internal/core/spec/types/env.go (1)
  • EnvValue (29-33)
internal/core/spec/builder.go (2)
  • StepBuildContext (25-28)
  • BuildContext (13-22)
internal/core/spec/dag.go (1)
  • Transformer (217-220)
internal/common/cmdutil/cmdutil.go (2)
  • SplitCommand (142-169)
  • JoinCommandArgs (20-22)
internal/core/spec/errors.go (7)
  • ErrInvalidSignal (21-21)
  • ErrStepCommandIsEmpty (29-29)
  • ErrStepCommandMustBeArrayOrString (30-30)
  • ErrExecutorTypeMustBeString (23-23)
  • ErrExecutorConfigValueMustBeMap (24-24)
  • ErrExecutorHasInvalidKey (25-25)
  • ErrExecutorConfigMustBeStringOrMap (26-26)
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest
🔇 Additional comments (24)
internal/core/spec/types/stringarray_test.go (4)

104-172: LGTM!

Excellent coverage of StringOrArray behavior when embedded in a struct. The test correctly distinguishes between unset values and explicitly set empty arrays (line 167), demonstrating proper zero-value semantics.


174-203: LGTM!

Appropriate coverage for the MailToValue alias. Since it's a type alias for StringOrArray, detailed behavior is tested in the main test function.


205-234: LGTM!

Appropriate coverage for the TagsValue alias. The test structure is consistent with TestMailToValue and adequately validates the alias behavior.


236-309: LGTM!

Excellent coverage of edge cases including numeric conversion, mixed types, invalid input handling, and raw value access. The tests comprehensively validate StringOrArray behavior across various scenarios.

internal/core/spec/types/continueon_test.go (3)

1-10: LGTM!

Imports are appropriate. Using types_test package for black-box testing is good practice, and the use of stretchr/testify/require and assert follows the coding guidelines.


218-281: LGTM!

Good integration test covering ContinueOnValue embedded within a struct. Tests the practical use case including when the field is not set (wantIsZero).


283-303: LGTM!

Good coverage of the Value() accessor for both string and map forms.

internal/core/spec/variables_test.go (2)

13-82: LGTM! Well-structured table-driven test.

The test correctly handles map iteration order non-determinism by using content-based verification (lines 68-79), covers multiple primitive types, and properly uses t.Parallel() throughout.


281-288: LGTM! Clean test helper.

Correctly uses t.Helper() and provides a concise way to construct EnvValue instances from YAML for testing.

internal/core/spec/types/env_test.go (1)

1-162: Well-structured test coverage for EnvValue.

Good use of table-driven tests with comprehensive coverage of map form, array forms, error cases, zero-value behavior, and in-struct unmarshalling. The test structure follows coding guidelines with t.Parallel() and stretchr/testify.

internal/core/spec/schema_test.go (1)

1-833: Comprehensive schema resolution test coverage.

Excellent test coverage for schema extraction, URL/file loading, resolution precedence (CWD vs workingDir vs DAG dir), validation, and defaults. Good use of httptest for HTTP mocking and table-driven tests.

internal/core/spec/schema.go (2)

94-153: Well-designed schema file resolution with clear precedence.

The tiered resolution strategy (CWD → workingDir → dagDir) is well-documented and the error aggregation provides good debugging information. The implementation correctly handles edge cases like whitespace-only paths.


155-174: Clean implementation of schema reference extraction.

The function safely handles non-map params, missing keys, and non-string values without panicking.

internal/core/spec/step.go (1)

182-213: Well-structured step build pipeline.

Good separation of concerns with the transformer pipeline for simple fields and explicit calls for complex transformations. Error aggregation via core.ErrorList provides comprehensive feedback.

internal/core/spec/types/schedule_test.go (1)

1-305: Comprehensive test coverage for ScheduleValue.

Excellent table-driven tests covering all input forms (string, array, map with start/stop/restart), error cases, and edge cases like null values and empty strings. Good use of t.Parallel() and proper assertions with stretchr/testify.

internal/core/spec/loader_test.go (3)

30-70: LGTM! Good refactoring to table-driven tests.

The consolidation of error test cases into a table-driven approach improves maintainability and follows Go testing best practices.


72-95: LGTM! MetadataOnly test correctly validates behavior.

The test properly verifies that:

  • Non-metadata fields from YAML are not populated in metadata-only mode
  • Default values are still applied via InitializeDefaults
  • Steps are not loaded

The assertions and comments clearly document the expected behavior.


668-810: LGTM! Comprehensive loader option tests.

The new TestLoadWithLoaderOptions test function provides excellent coverage of various loader options including:

  • WithDAGsDir for sub-DAG discovery
  • WithAllowBuildErrors for capturing build-time errors
  • SkipSchemaValidation for bypassing schema validation
  • WithSkipBaseHandlers for excluding base config handlers
  • WithParamsAsList for list-based parameter handling

The tests are well-structured and properly use t.Parallel().

internal/core/spec/params_test.go (1)

1-662: LGTM! Comprehensive parameter parsing test suite.

This new test file provides excellent coverage of parameter handling across all parsing functions:

  • paramPair formatting (String/Escaped)
  • String, list, and map parameter parsing
  • Parameter value parsing with various types
  • Parameter overriding by name and position
  • End-to-end parameter parsing with Eval/NoEval modes
  • Parameter value evaluation with variable substitution

The tests are well-structured using table-driven patterns and properly use t.Parallel() for concurrent execution.

internal/core/spec/dag_test.go (3)

15-44: LGTM! Well-designed helper functions for test data creation.

The helper functions (testBuildContext, portValue, stringOrArray, stringOrArrayList) provide clean utilities for creating test data. Using YAML unmarshaling in the helpers ensures test data accurately reflects real-world parsing behavior.


46-1761: LGTM! Comprehensive DAG building test coverage.

This test file provides excellent coverage of all DAG building functions with:

  • Extensive tests for all field builders (type, name, group, timeout, tags, workers, containers, SSH, secrets, handlers, etc.)
  • Good balance of success and error test cases
  • Proper use of table-driven tests for similar scenarios
  • Correct handling of t.Setenv tests (avoiding t.Parallel() where needed)
  • Clear test names and assertions

The tests validate both the transformation logic and error handling for the new manifest-based spec build flow.


1113-1200: Implement missing shell helper functions for tests.

The tests reference shellValue and shellValueArray helper functions (lines 1113, 1119, 1157, 1172, 1187) that are not defined in the file. Implement these helpers following the pattern of existing helpers like portValue, stringOrArray, and stringOrArrayList already in the test file. These should construct types.ShellValue objects from string and array inputs respectively.

internal/core/spec/dag.go (2)

215-323: LGTM! Well-designed transformer pipeline.

The transformer pipeline provides a clean, extensible architecture:

  • Generic Transformer interface with type-safe implementations
  • Clear separation of metadata transformers (always run) vs full transformers (run when not in metadata-only mode)
  • Consistent error handling via wrapTransformError
  • Reflective field setting allows declarative transformer registration

This design makes it easy to add new DAG fields by simply adding a new transformer to the appropriate list.


382-1157: LGTM! Comprehensive and well-structured builder functions.

The individual builder functions demonstrate good practices:

  • Type validation: buildType validates against allowed values and provides clear error messages for unsupported types (lines 384-397)
  • Flexible input handling: Registry auths support multiple formats (string, map[string]any, map[any]any) with appropriate type conversions (lines 801-871)
  • Environment expansion: Consistently respects BuildFlagNoEval across functions (e.g., lines 666-687, 715-716)
  • Backward compatibility: Container builder handles deprecated WorkDir field (lines 792-796)
  • Clear error reporting: Uses core.ValidationError with field names and values for debugging (e.g., line 755)
  • Deprecation handling: Handlers support both Abort (canonical) and Cancel (deprecated) with conflict detection (lines 946-962)

The builders provide a solid foundation for the new manifest-based DAG spec transformation.

Comment on lines +71 to +78
req, err := http.NewRequest("GET", schemaURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

HTTP request lacks timeout, risking indefinite blocking.

loadSchemaFromURL uses http.DefaultClient which has no default timeout. If the remote server is unresponsive, this could block the DAG loading process indefinitely.

🔎 Proposed fix
+import (
+	"context"
+	"time"
+	// ... other imports
+)

+const schemaFetchTimeout = 30 * time.Second

 func loadSchemaFromURL(schemaURL string) (data []byte, err error) {
 	// Validate URL to prevent potential security issues (and satisfy linter :P)
 	parsedURL, err := url.Parse(schemaURL)
 	if err != nil {
 		return nil, fmt.Errorf("invalid URL: %w", err)
 	}
 	if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
 		return nil, fmt.Errorf("unsupported URL scheme: %s", parsedURL.Scheme)
 	}

-	req, err := http.NewRequest("GET", schemaURL, nil)
+	ctx, cancel := context.WithTimeout(context.Background(), schemaFetchTimeout)
+	defer cancel()
+
+	req, err := http.NewRequestWithContext(ctx, "GET", schemaURL, nil)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create request: %w", err)
 	}

-	resp, err := http.DefaultClient.Do(req)
+	client := &http.Client{Timeout: schemaFetchTimeout}
+	resp, err := client.Do(req)
📝 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.

Suggested change
req, err := http.NewRequest("GET", schemaURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
ctx, cancel := context.WithTimeout(context.Background(), schemaFetchTimeout)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", schemaURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
client := &http.Client{Timeout: schemaFetchTimeout}
resp, err := client.Do(req)
if err != nil {
return nil, err
🤖 Prompt for AI Agents
In internal/core/spec/schema.go around lines 71-78, the code uses
http.DefaultClient.Do without a timeout which can block indefinitely; replace
that call by creating a dedicated http.Client with a reasonable Timeout (e.g.
5–15s) or perform the request with a context tied to context.WithTimeout and use
http.NewRequestWithContext, then call client.Do(ctxReq); ensure you cancel the
context (if using WithTimeout) and still handle/close resp.Body as before.

Comment on lines +208 to +218
t.Run("WithEvaluation", func(t *testing.T) {
ctx := BuildContext{
ctx: context.Background(),
opts: BuildOpts{},
}

input := map[string]any{"GREETING": "hello"}
result, err := loadVariables(ctx, input)
require.NoError(t, err)
assert.Equal(t, "hello", result["GREETING"])
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent parallelization: missing t.Parallel() in subtests.

The WithEvaluation and VariableReference subtests don't use t.Parallel(), while all other subtests in this function do. Since each creates a fresh BuildContext with no apparent shared state, this appears unintentional.

🔎 Suggested fix
 	t.Run("WithEvaluation", func(t *testing.T) {
+		t.Parallel()
 		ctx := BuildContext{
 	t.Run("VariableReference", func(t *testing.T) {
+		t.Parallel()
 		ctx := BuildContext{

Also applies to: 235-250

🤖 Prompt for AI Agents
In internal/core/spec/variables_test.go around lines 208-218 (and also for the
similar subtest at lines 235-250), the subtests "WithEvaluation" and
"VariableReference" are missing t.Parallel() while other subtests run in
parallel; add t.Parallel() as the first statement inside each of these subtest
functions so they run concurrently like the others, ensuring no shared state is
used or created before calling t.Parallel().

Comment on lines +343 to +355
t.Run("WithEvaluation", func(t *testing.T) {
ctx := BuildContext{
ctx: context.Background(),
opts: BuildOpts{},
}

env := envValueFromYAML(t, `
GREETING: hello
`)
result, err := loadVariablesFromEnvValue(ctx, env)
require.NoError(t, err)
assert.Equal(t, "hello", result["GREETING"])
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent parallelization: missing t.Parallel() in subtests.

Same pattern as TestLoadVariables—the WithEvaluation and VariableReference subtests lack t.Parallel() while all others use it. Since each creates an independent BuildContext, this appears unintentional.

🔎 Suggested fix
 	t.Run("WithEvaluation", func(t *testing.T) {
+		t.Parallel()
 		ctx := BuildContext{
 	t.Run("VariableReference", func(t *testing.T) {
+		t.Parallel()
 		ctx := BuildContext{

Also applies to: 373-387

🤖 Prompt for AI Agents
internal/core/spec/variables_test.go around lines 343-355 (and similarly
373-387): the two subtests "WithEvaluation" and "VariableReference" are missing
t.Parallel() causing inconsistent parallelization with other subtests; add a
call to t.Parallel() as the first statement inside each of these subtest
functions so they run in parallel like the others, ensuring no shared state is
introduced and tests remain independent.

Copy link
Copy Markdown

@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

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce71a87 and a6de690.

📒 Files selected for processing (1)
  • internal/runtime/agent/agent.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (AGENTS.md)

**/*.go: Backend entrypoint in cmd/ orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under internal/* (for example internal/runtime, internal/persistence)
Keep Go files gofmt/goimports clean; use tabs, PascalCase for exported symbols (SchedulerClient), lowerCamelCase for locals, and Err... names for package-level errors
Repository linting relies on golangci-lint; prefer idiomatic Go patterns, minimal global state, and structured logging helpers in internal/common

Files:

  • internal/runtime/agent/agent.go
⏰ 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). (1)
  • GitHub Check: Test on ubuntu-latest

coordinatorCli := a.createCoordinatorClient(ctx)

// Resolve secrets if defined
a.dag.LoadDotEnv(ctx)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle error from LoadDotEnv call.

The call to a.dag.LoadDotEnv(ctx) at line 295 doesn't capture or handle potential errors. Similar dotenv loading operations in Go return errors that should be checked, particularly for file I/O operations. Failures would be silently ignored, potentially leading to incomplete environment setup affecting downstream DAG execution and secret resolution.

If LoadDotEnv returns an error, add appropriate error handling:

if err := a.dag.LoadDotEnv(ctx); err != nil {
	logger.Warn(ctx, "Failed to load .env file", tag.Error(err))
	// Or return err if .env loading should be mandatory
}
🤖 Prompt for AI Agents
In internal/runtime/agent/agent.go around line 295 the call to
a.dag.LoadDotEnv(ctx) ignores any returned error; update the call to capture the
error and handle it appropriately by checking if err != nil and then either log
a warning with the error (e.g., logger.Warn(ctx, "Failed to load .env file",
tag.Error(err))) or return the error if loading must be mandatory so upstream
callers can handle it.

@yottahmd yottahmd merged commit d87b8c6 into main Dec 22, 2025
5 checks passed
@yottahmd yottahmd deleted the refactor-core branch December 22, 2025 17:19
@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 22, 2025

Codecov Report

❌ Patch coverage is 89.26863% with 157 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.75%. Comparing base (d2b3d8f) to head (a6de690).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
internal/core/spec/step.go 84.18% 51 Missing and 20 partials ⚠️
internal/core/spec/dag.go 93.92% 18 Missing and 14 partials ⚠️
internal/core/spec/schema.go 83.52% 8 Missing and 6 partials ⚠️
internal/core/spec/types/continueon.go 91.73% 9 Missing and 1 partial ⚠️
internal/core/spec/types/shell.go 83.33% 6 Missing and 1 partial ⚠️
internal/core/spec/types/port.go 82.75% 4 Missing and 1 partial ⚠️
internal/core/spec/types/schedule.go 92.18% 4 Missing and 1 partial ⚠️
internal/core/spec/types/stringarray.go 82.75% 4 Missing and 1 partial ⚠️
internal/core/spec/loader.go 88.23% 2 Missing and 2 partials ⚠️
internal/core/spec/types/env.go 95.55% 1 Missing and 1 partial ⚠️
... and 1 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1499      +/-   ##
==========================================
+ Coverage   60.13%   61.75%   +1.61%     
==========================================
  Files         196      203       +7     
  Lines       21916    22146     +230     
==========================================
+ Hits        13180    13677     +497     
+ Misses       7341     7095     -246     
+ Partials     1395     1374      -21     
Files with missing lines Coverage Δ
internal/core/spec/builder.go 82.06% <100.00%> (+13.65%) ⬆️
internal/core/spec/params.go 91.97% <ø> (+17.22%) ⬆️
internal/core/spec/schedule.go 100.00% <ø> (+12.12%) ⬆️
internal/core/validator.go 47.31% <100.00%> (+4.95%) ⬆️
internal/runtime/agent/agent.go 50.00% <100.00%> (+0.09%) ⬆️
internal/core/spec/types/env.go 95.55% <95.55%> (ø)
internal/core/spec/variables.go 79.66% <86.66%> (+18.29%) ⬆️
internal/core/spec/loader.go 74.55% <88.23%> (+10.32%) ⬆️
internal/core/spec/types/port.go 82.75% <82.75%> (ø)
internal/core/spec/types/schedule.go 92.18% <92.18%> (ø)
... and 6 more

... and 5 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d2b3d8f...a6de690. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant