Skip to content

Conversation

@yottahmd
Copy link
Collaborator

@yottahmd yottahmd commented Dec 9, 2025

Summary by CodeRabbit

  • New Features

    • Service-scoped configuration loader with explicit per-command flag binding and a loader-based API.
  • Bug Fixes

    • More robust parsing of environment and key=value inputs; malformed entries are ignored.
    • Logging, TLS, peer, scheduler and timezone settings consistently read from the new Core config namespace.
  • Refactor

    • Public config field renamed: Global → Core — update config files and tools to use Core.* fields.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Warning

Rate limit exceeded

@yottahmd has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 17 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3fcc1ea and 59f72bd.

📒 Files selected for processing (2)
  • internal/common/config/loader.go (15 hunks)
  • internal/common/config/loader_test.go (12 hunks)

Walkthrough

Replaces Config.Global with Config.Core, adds a service-aware ConfigLoader owning per-command Viper instances and service bitflags, makes flag binding explicit to a Viper instance, and standardizes many "key=value" parsing sites from strings.SplitN to strings.Cut.

Changes

Cohort / File(s) Summary
Config API & usages (Global → Core)
internal/common/config/config.go, internal/common/config/context.go, internal/common/config/timzone.go, internal/common/config/context_test.go, internal/common/config/timezone_test.go, internal/test/helper.go, internal/runtime/subcmd.go, internal/runtime/subcmd_test.go, internal/service/worker/handler_test.go, internal/cmd/agent_executor_test.go
Replaced public Global with Core on Config and updated call sites/tests to use cfg.Core.* (Debug, LogFormat, TZ, Location, TzOffsetInSec, BaseEnv, DefaultShell, Peer, SkipExamples).
Config loader & service-scoped loading
internal/common/config/loader.go, internal/common/config/loader_test.go, internal/cmd/context.go, internal/cmd/flags.go, internal/test/helper.go
Introduced Service type/constants and ConfigSection bitflags; added ConfigLoader that owns a *viper.Viper with NewConfigLoader(v, ...), WithService(...), and Load(); loader-driven per-section loading, env bindings, defaults, and finalize/validation.
Command context & command changes
internal/cmd/context.go, internal/cmd/agent_executor.go, internal/cmd/coordinator.go, internal/cmd/dry.go, internal/cmd/restart.go, internal/cmd/retry.go, internal/cmd/scheduler.go, internal/cmd/start.go, internal/cmd/exec.go, internal/cmd/agent_executor_test.go, internal/cmd/flags.go, internal/cmd/agent_executor_test.go
NewContext creates per-command viper.Viper, uses NewConfigLoader with a service selection; commands now read settings from cfg.Core.*. Added/updated runExec and executeRetry, updated logger setup to assign logger into context, and switched worker-label parsing to strings.Cut.
String parsing standardization (SplitN → Cut)
internal/common/collections/syncmap.go, internal/common/config/env.go, internal/common/config/env_test.go, internal/common/masking/masker.go, internal/common/stringutil/kv.go, internal/core/dag.go, internal/core/execution/context.go, internal/core/spec/builder_test.go, internal/core/spec/variables.go, internal/integration/parallel_test.go, internal/integration/base_dag_test.go, internal/runtime/agent/dbclient.go, internal/runtime/env.go, internal/runtime/env_test.go, internal/runtime/runner.go, internal/service/frontend/server.go, internal/service/scheduler/scheduler.go, internal/integration/*, internal/common/*
Standardized many strings.SplitN(..., "=", 2) uses to strings.Cut(..., "=") and use the returned found boolean to guard insertion/assignments, adjusting edge-case handling across code and tests.
Tests & helpers updated for loader/Core
internal/common/config/loader_test.go, internal/common/config/context_test.go, internal/common/config/env_test.go, internal/common/config/timezone_test.go, internal/runtime/subcmd_test.go, internal/core/spec/builder_test.go, internal/runtime/env_test.go, internal/test/helper.go, internal/cmd/agent_executor_test.go, internal/common/stringutil/kv_test.go
Tests updated to use Config.Core fields, added loader test helpers (testLoad, testLoadWithError), adapted expectations to strings.Cut behavior, and enabled additional parallelization in kv tests.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CLI as CLI (cobra)
    participant V as Viper (per-command)
    participant Loader as ConfigLoader
    participant Ctx as NewContext
    participant Cmd as Command

    CLI->>V: initFlags(cmd) (create & register flags)
    CLI->>V: bindFlags(v, cmd, ...) (bind flags under lock)
    CLI->>Loader: NewConfigLoader(v, WithService(service))
    Loader->>V: bind env vars, set defaults
    Loader->>Loader: loadCoreConfig()
    alt service requires additional sections
        Loader->>Loader: loadPathsConfig()/loadServerConfig()/...
    end
    Loader->>Ctx: Load() -> *Config (cfg.Core populated)
    Ctx->>Cmd: initialize logger/agents using cfg.Core.*
    Cmd->>Cmd: execute command logic (agent, scheduler, exec, etc.)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay special attention to internal/common/config/loader.go (service gating, env binding, defaults, finalize).
  • Verify internal/cmd/context.go for per-command Viper lifecycle, correct service selection, and thread-safe bindFlags.
  • Audit parsing sites converted to strings.Cut to ensure correct handling when found == false.
  • Search for any residual Global references to ensure complete migration.

Possibly related PRs

Poem

🐰 I hopped from Global down to Core,
Viper in paw, flags tied to the door,
Cutting strings tidy — no more slice fright,
Services wake and configs hum at night,
I thump in joy — the loader runs just right.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.49% 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(config): load only necessary config for context' accurately and specifically summarizes the main change in the changeset, which involves refactoring the configuration loading system to load only the necessary config sections for each context.

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

Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
internal/common/stringutil/kv.go (1)

53-63: Comment doesn't match implementation regarding empty keys.

The documentation states "with non-empty key" but the code doesn't enforce this. An entry like "=VALUE" would insert map[""] = "VALUE". Additionally, the phrase "exactly one '='" is misleading since strings.Cut splits on the first occurrence, meaning "KEY=VAL=MORE" is accepted.

Apply this diff to enforce non-empty keys as documented:

 func KeyValuesToMap(kvSlice []string) map[string]string {
 	result := make(map[string]string, len(kvSlice))
 	for _, kv := range kvSlice {
 		key, value, found := strings.Cut(kv, "=")
-		if found {
+		if found && key != "" {
 			result[key] = value
 		}
 	}
 	return result
 }

Alternatively, update the comment to reflect that empty keys are allowed (though this is likely not the intended behavior for most use cases).

🧹 Nitpick comments (3)
internal/common/config/loader.go (3)

121-129: Parameter shadows imported package name.

The parameter viper shadows the imported viper package, which can be confusing. Consider renaming to v or viperInstance for clarity.

 // NewConfigLoader creates a new ConfigLoader instance with an isolated viper instance
 // and applies all given options.
-func NewConfigLoader(viper *viper.Viper, options ...ConfigLoaderOption) *ConfigLoader {
-	loader := &ConfigLoader{v: viper}
+func NewConfigLoader(v *viper.Viper, options ...ConfigLoaderOption) *ConfigLoader {
+	loader := &ConfigLoader{v: v}
 	for _, option := range options {
 		option(loader)
 	}
 	return loader
 }

920-927: Mutating environment variables as side effect.

Calling os.Setenv to normalize path values modifies the process environment, which could have unintended side effects on other components or child processes. Consider normalizing paths during value retrieval in the config loading phase instead of mutating the environment.


838-861: Clarify precedence for duplicate environment variable bindings.

Multiple bindings exist for the same config keys (e.g., ui.maxDashboardPageLimit → both UI_MAX_DASHBOARD_PAGE_LIMIT and MAX_DASHBOARD_PAGE_LIMIT; auth.basic.username → both AUTH_BASIC_USERNAME and BASICAUTH_USERNAME).

When both legacy and new environment variables are set, the behavior depends on viper's binding order. Consider documenting which takes precedence, or logging a warning when conflicting env vars are detected.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4b8484 and d648232.

📒 Files selected for processing (39)
  • internal/cmd/agent_executor.go (1 hunks)
  • internal/cmd/agent_executor_test.go (1 hunks)
  • internal/cmd/context.go (8 hunks)
  • internal/cmd/coordinator.go (1 hunks)
  • internal/cmd/dry.go (1 hunks)
  • internal/cmd/exec.go (1 hunks)
  • internal/cmd/flags.go (1 hunks)
  • internal/cmd/restart.go (1 hunks)
  • internal/cmd/retry.go (1 hunks)
  • internal/cmd/scheduler.go (1 hunks)
  • internal/cmd/start.go (1 hunks)
  • internal/common/collections/syncmap.go (1 hunks)
  • internal/common/config/config.go (2 hunks)
  • internal/common/config/context.go (1 hunks)
  • internal/common/config/context_test.go (1 hunks)
  • internal/common/config/env.go (1 hunks)
  • internal/common/config/env_test.go (1 hunks)
  • internal/common/config/loader.go (15 hunks)
  • internal/common/config/loader_test.go (12 hunks)
  • internal/common/config/timezone_test.go (4 hunks)
  • internal/common/config/timzone.go (1 hunks)
  • internal/common/masking/masker.go (1 hunks)
  • internal/common/stringutil/kv.go (2 hunks)
  • internal/core/dag.go (1 hunks)
  • internal/core/execution/context.go (1 hunks)
  • internal/core/spec/builder_test.go (2 hunks)
  • internal/core/spec/variables.go (1 hunks)
  • internal/integration/base_dag_test.go (2 hunks)
  • internal/integration/parallel_test.go (1 hunks)
  • internal/runtime/agent/dbclient.go (1 hunks)
  • internal/runtime/env.go (4 hunks)
  • internal/runtime/env_test.go (2 hunks)
  • internal/runtime/runner.go (2 hunks)
  • internal/runtime/subcmd.go (1 hunks)
  • internal/runtime/subcmd_test.go (1 hunks)
  • internal/service/frontend/server.go (2 hunks)
  • internal/service/scheduler/scheduler.go (1 hunks)
  • internal/service/worker/handler_test.go (1 hunks)
  • internal/test/helper.go (4 hunks)
🧰 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/integration/base_dag_test.go
  • internal/runtime/agent/dbclient.go
  • internal/cmd/coordinator.go
  • internal/cmd/exec.go
  • internal/common/config/timezone_test.go
  • internal/common/config/timzone.go
  • internal/runtime/subcmd.go
  • internal/cmd/start.go
  • internal/runtime/env.go
  • internal/common/config/env_test.go
  • internal/cmd/agent_executor.go
  • internal/cmd/retry.go
  • internal/common/masking/masker.go
  • internal/cmd/agent_executor_test.go
  • internal/integration/parallel_test.go
  • internal/core/spec/builder_test.go
  • internal/common/stringutil/kv.go
  • internal/core/execution/context.go
  • internal/cmd/dry.go
  • internal/core/dag.go
  • internal/service/frontend/server.go
  • internal/service/scheduler/scheduler.go
  • internal/cmd/flags.go
  • internal/common/config/context.go
  • internal/cmd/scheduler.go
  • internal/common/collections/syncmap.go
  • internal/service/worker/handler_test.go
  • internal/runtime/subcmd_test.go
  • internal/common/config/context_test.go
  • internal/common/config/env.go
  • internal/common/config/config.go
  • internal/runtime/runner.go
  • internal/runtime/env_test.go
  • internal/core/spec/variables.go
  • internal/cmd/restart.go
  • internal/cmd/context.go
  • internal/test/helper.go
  • internal/common/config/loader_test.go
  • internal/common/config/loader.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/integration/base_dag_test.go
  • internal/common/config/timezone_test.go
  • internal/common/config/env_test.go
  • internal/cmd/agent_executor_test.go
  • internal/integration/parallel_test.go
  • internal/core/spec/builder_test.go
  • internal/service/worker/handler_test.go
  • internal/runtime/subcmd_test.go
  • internal/common/config/context_test.go
  • internal/runtime/env_test.go
  • internal/common/config/loader_test.go
🧠 Learnings (3)
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*_test.go : Co-locate Go tests as `*_test.go`; favour table-driven cases and cover failure paths

Applied to files:

  • internal/common/config/timezone_test.go
  • internal/integration/parallel_test.go
  • internal/common/config/loader_test.go
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/common/config/timezone_test.go
  • internal/integration/parallel_test.go
  • internal/common/config/context_test.go
  • internal/common/config/loader_test.go
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*.go : Backend entrypoint in `cmd/` orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under `internal/*` (for example `internal/runtime`, `internal/persistence`)

Applied to files:

  • internal/cmd/context.go
🧬 Code graph analysis (23)
internal/integration/base_dag_test.go (1)
internal/common/config/config.go (2)
  • Core (53-81)
  • Peer (286-301)
internal/cmd/coordinator.go (1)
internal/common/config/config.go (3)
  • Core (53-81)
  • Peer (286-301)
  • TLSConfig (233-237)
internal/common/config/timezone_test.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/timzone.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/runtime/subcmd.go (2)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/env.go (1)
  • BaseEnv (9-11)
internal/cmd/start.go (1)
internal/common/config/config.go (3)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
internal/cmd/agent_executor.go (2)
internal/common/config/config.go (2)
  • Config (9-39)
  • Core (53-81)
internal/common/logger/logger.go (4)
  • Config (45-50)
  • WithDebug (55-59)
  • WithQuiet (76-80)
  • WithFormat (62-66)
internal/cmd/retry.go (1)
internal/common/config/config.go (2)
  • Core (53-81)
  • Peer (286-301)
internal/cmd/agent_executor_test.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/cmd/dry.go (1)
internal/common/config/config.go (3)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
internal/service/frontend/server.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/service/scheduler/scheduler.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/context.go (2)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/env.go (1)
  • BaseEnv (9-11)
internal/cmd/scheduler.go (1)
internal/common/config/config.go (2)
  • Config (9-39)
  • Core (53-81)
internal/service/worker/handler_test.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/runtime/subcmd_test.go (1)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/context_test.go (2)
internal/common/config/config.go (1)
  • Core (53-81)
internal/common/config/env.go (1)
  • BaseEnv (9-11)
internal/runtime/runner.go (3)
internal/common/logger/context.go (1)
  • Error (50-52)
internal/runtime/eval.go (1)
  • EvalString (14-16)
internal/common/cmdutil/eval.go (1)
  • EvalString (126-190)
internal/runtime/env_test.go (3)
internal/runtime/env.go (1)
  • Env (24-54)
internal/runtime/eval.go (1)
  • EvalString (14-16)
internal/common/collections/syncmap.go (1)
  • SyncMap (10-14)
internal/core/spec/variables.go (1)
internal/core/spec/errors.go (1)
  • ErrInvalidEnvValue (27-27)
internal/cmd/context.go (3)
internal/common/config/config.go (3)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
internal/common/config/loader.go (3)
  • WithService (79-83)
  • NewConfigLoader (123-129)
  • Service (21-21)
internal/common/config/context.go (1)
  • WithConfig (12-14)
internal/test/helper.go (2)
internal/common/config/loader.go (1)
  • NewConfigLoader (123-129)
internal/common/config/config.go (3)
  • Core (53-81)
  • Config (9-39)
  • Peer (286-301)
internal/common/config/loader.go (3)
internal/common/config/definition.go (1)
  • Definition (5-138)
internal/common/config/config.go (9)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
  • Server (84-121)
  • UI (207-213)
  • Coordinator (252-257)
  • Scheduler (267-283)
  • Auth (144-150)
  • AuthToken (177-179)
internal/common/config/path.go (1)
  • Paths (13-30)
⏰ 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 (61)
internal/common/stringutil/kv.go (2)

15-18: LGTM! Clean refactor to strings.Cut.

The use of strings.Cut simplifies the logic and correctly handles cases where no "=" delimiter exists by returning the entire string as the key.


20-26: LGTM! Correct handling of delimiter presence.

The conditional return based on found ensures that entries without "=" correctly return an empty value.

internal/common/collections/syncmap.go (1)

29-32: LGTM: Improved key-value parsing with strings.Cut.

The refactor from strings.SplitN to strings.Cut is cleaner and more efficient. The found flag properly guards against malformed entries (strings without "="), ensuring only valid key-value pairs are inserted.

internal/common/masking/masker.go (1)

76-80: LGTM: Consistent use of strings.Cut for env parsing.

The migration to strings.Cut is consistent with the broader refactor and improves clarity. Returning empty strings when "=" is not found is more explicit than checking array bounds.

internal/runtime/agent/dbclient.go (1)

44-47: LGTM: Output variable parsing aligned with refactor.

The use of strings.Cut with the found guard is consistent with the repository-wide refactor and correctly handles output variable parsing.

internal/runtime/env_test.go (2)

371-372: LGTM: Test correctly uses strings.Cut for env parsing.

The test properly mirrors the production code refactor, extracting key-value pairs and handling evaluated values.

Also applies to: 377-377


399-400: LGTM: Consistent env parsing in test setup.

The test setup correctly uses strings.Cut to parse and evaluate step environment variables, consistent with the refactor pattern.

Also applies to: 406-407

internal/integration/base_dag_test.go (1)

72-72: LGTM: Config namespace migration to Core.

The change from Global.Peer to Core.Peer is consistent with the PR's config refactor objectives.

Also applies to: 186-186

internal/common/config/context.go (1)

36-36: LGTM: BaseEnv access migrated to Core namespace.

The change from Global.BaseEnv to Core.BaseEnv aligns with the config refactor and is straightforward.

internal/cmd/start.go (1)

491-491: LGTM: Peer config migrated to Core namespace.

The change from Global.Peer to Core.Peer is consistent with the config refactor across the codebase.

internal/cmd/dry.go (1)

102-102: LGTM: Peer config migrated to Core namespace.

The change from Global.Peer to Core.Peer aligns with the PR's config namespace refactor.

internal/service/frontend/server.go (2)

75-87: Frontend funcsConfig: TZ/TzOffsetInSec now sourced from Core – consistent with new config model

Switching TZ and TzOffsetInSec to cfg.Core.* aligns this server with the new Core configuration struct; types and semantics remain unchanged.


149-157: HTTP request logger now reads log format from Core.LogFormat – LGTM

Using srv.config.Core.LogFormat == "json" to control structured logging matches the Core-based config refactor and keeps behavior equivalent to the previous Global-based check.

internal/cmd/scheduler.go (1)

42-45: Scheduler logging: log-format now read from Core.LogFormat – OK

The info log during scheduler initialization correctly pulls ctx.Config.Core.LogFormat, consistent with the new Core config struct.

internal/cmd/retry.go (1)

166-183: Retry agent: peer config switched to Core.Peer – consistent with Core migration

Passing ctx.Config.Core.Peer into agent.New maintains the expected peer TLS configuration and aligns this call site with the new Core-based config API.

internal/cmd/agent_executor_test.go (1)

104-115: Tests updated to use Config.Core – matches new public config struct

Initializing Config.Core{Debug, LogFormat} mirrors the new config.Config shape and keeps configureLoggerForProgress tests exercising the right fields.

internal/common/config/env.go (1)

40-52: Env filtering: strings.Cut-based parsing is a safe, clearer improvement

Using strings.Cut to derive key and skipping entries without "=" simplifies the code and avoids relying on malformed environment entries; allowed-key and prefix checks remain unchanged.

internal/common/config/config.go (1)

10-12: Config API: introduction of Core field/type looks coherent with repo-wide usage

Defining an exported Core struct and hanging it off Config.Core cleanly encapsulates global settings (debug, log format, TZ/location, peer, base env). The field and type are well-documented and line up with all observed call sites in commands and services.

Also applies to: 52-81

internal/service/scheduler/scheduler.go (1)

75-78: Scheduler time location: now taken from Core.Location – behavior preserved

Using cfg.Core.Location (falling back to time.Local when nil) is consistent with the new Core config and maintains the same defaulting semantics for the scheduler.

internal/cmd/restart.go (1)

150-162: Restart agent: peer config migrated to Core.Peer – consistent and correct

Routing ctx.Config.Core.Peer into agent.New brings the restart flow in line with the Core-based config model without changing agent behavior.

internal/cmd/exec.go (1)

165-169: LGTM! Clean refactor to strings.Cut.

The migration from strings.SplitN to strings.Cut is more idiomatic and improves readability. The validation logic correctly checks found, key, and value to ensure proper key=value format.

internal/runtime/subcmd.go (1)

29-29: LGTM! Configuration namespace migration.

The change from cfg.Global.BaseEnv to cfg.Core.BaseEnv aligns with the broader refactor migrating configuration to the Core namespace.

internal/cmd/coordinator.go (1)

146-151: LGTM! TLS configuration migrated to Core namespace.

The TLS configuration now correctly reads from cfg.Core.Peer.* fields, aligning with the project-wide configuration refactor.

internal/common/config/timezone_test.go (1)

14-64: LGTM! Test updated for Core-based configuration.

All test cases correctly use &Core{} instead of &Global{}, aligning with the updated setTimezone function signature.

internal/core/dag.go (1)

352-355: LGTM! Cleaner parsing with strings.Cut.

The refactor from strings.SplitN to strings.Cut improves readability and is more idiomatic. The behavior remains the same: only parameters containing "=" are included in the map.

internal/common/config/timzone.go (1)

9-9: LGTM! Function signature updated for Core configuration.

The parameter type change from *Global to *Core aligns with the configuration refactor. The function body remains unchanged as the required fields exist in the Core struct.

internal/integration/parallel_test.go (1)

970-974: LGTM! Test helper refactored to use strings.Cut.

The parsing logic is cleaner with strings.Cut. The found flag provides explicit validation, and the code is more readable than array indexing.

internal/cmd/agent_executor.go (1)

72-77: LGTM! Logger configuration updated to Core namespace.

The logger configuration now correctly reads from ctx.Config.Core.Debug and ctx.Config.Core.LogFormat, consistent with the configuration refactor.

internal/runtime/subcmd_test.go (1)

24-26: LGTM! Clean migration to Core-based configuration.

The test correctly updates from Global to Core configuration namespace, aligning with the new public API structure.

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

38-42: LGTM! More idiomatic string parsing.

The migration from strings.SplitN to strings.Cut is cleaner and more readable for extracting key-value pairs. The found flag makes the intent explicit.

internal/core/execution/context.go (1)

37-40: LGTM! Consistent key-value parsing.

The strings.Cut approach correctly handles environment variable parsing, skipping entries without "=" delimiters. This aligns with the broader migration to Cut-based parsing across the codebase.

internal/common/config/env_test.go (1)

50-53: LGTM! Test helper aligns with production code.

The test correctly adopts strings.Cut for environment variable parsing, matching the updated production code patterns.

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

3657-3660: LGTM! Consistent test updates.

Both test segments correctly adopt strings.Cut for environment variable parsing, maintaining consistency with the broader codebase refactor.

Also applies to: 3685-3688

internal/common/config/context_test.go (1)

72-72: LGTM! Correct configuration namespace update.

The test properly updates from Global to Core configuration, aligning with the new API structure.

internal/runtime/runner.go (1)

473-490: LGTM! Improved environment variable evaluation logic.

The refactor correctly:

  • Uses strings.Cut for cleaner key-value extraction
  • Evaluates only the value portion (not the entire "KEY=value" string), which is the correct behavior
  • Maintains proper storage format as KEY=evaluatedValue
  • Adds appropriate error logging for malformed entries

This is a logical improvement over the previous approach.

internal/service/worker/handler_test.go (1)

203-205: LGTM! Test configuration updated correctly.

The test properly migrates from Global to Core configuration namespace, consistent with the broader refactor.

internal/runtime/env.go (3)

77-82: LGTM! Clean refactor to strings.Cut.

The use of strings.Cut is more idiomatic than strings.SplitN and the found check properly handles entries without an = separator.


117-121: LGTM! Consistent use of strings.Cut for param parsing.

The pattern correctly stores the full param string (in key=value format) when a separator is found, using the extracted key as the map key.


390-394: LGTM! Consistent refactor to strings.Cut.

The change aligns with the rest of the codebase and properly handles the found flag to skip malformed entries.

internal/cmd/flags.go (1)

294-304: LGTM! Explicit Viper instance injection.

Passing an explicit *viper.Viper instance enables per-command configuration isolation and aligns with the new ConfigLoader pattern. This improves testability and avoids global state pollution.

internal/cmd/context.go (6)

60-68: LGTM! Migration to Core.* configuration paths.

The logger options now correctly reference Core.Debug and Core.LogFormat instead of the removed Global.* paths.


80-81: LGTM! Isolated Viper instance for command context.

Creating a fresh viper.New() instance and passing it to bindFlags prevents configuration leakage between commands and improves testability.


108-112: LGTM! Service-scoped configuration loading.

The serviceForCommand helper enables loading only necessary configuration sections per command, which improves startup performance and reduces configuration coupling.


231-235: LGTM! Migration to Core.Peer for coordinator client.

The peer configuration is now correctly sourced from Core.Peer instead of Global.Peer.


291-291: LGTM! Migration to Core.SkipExamples.

The DAG store option now correctly references Core.SkipExamples.


181-199: Verify command name coverage for service mapping

Coverage is comprehensive and correct. All CLI commands are properly accounted for: infrastructure commands (server, scheduler, worker, coordinator) have explicit mappings; agent commands (start, restart, retry, dry, exec) map to ServiceAgent; queue operations (enqueue, dequeue) and utility commands (version, validate, status, stop, start-all, migrate) appropriately use the ServiceNone default, which loads all config sections. This is a safe and efficient approach.

internal/test/helper.go (4)

130-137: LGTM! Test helper migrated to new loader pattern.

The test helper now creates an isolated Viper instance and uses the new ConfigLoader pattern. The Core.* field assignments for timezone settings are correct.


232-238: LGTM! Config file writer uses Core.* paths.

The helper config file generation correctly sources values from Core.Debug, Core.LogFormat, Core.DefaultShell, and Core.TZ.


505-507: LGTM! Consistent strings.Cut usage in test assertions.

The parsing logic correctly uses strings.Cut and validates that the output is in key=value format before checking the value.


567-568: LGTM! Agent construction uses Core.Peer.

The peer configuration is correctly sourced from Core.Peer for agent construction in tests.

internal/common/config/loader_test.go (6)

15-29: LGTM! Well-designed test helpers.

The testLoad and testLoadWithError helpers properly use t.Helper() for better stack traces and create isolated Viper instances per test. This aligns with the new ConfigLoader pattern and prevents test pollution.

Based on learnings, using stretchr/testify/require is the preferred approach.


142-161: LGTM! Test expectations updated for Core namespace.

The expected config structure correctly uses Core instead of Global, with all relevant fields (Debug, LogFormat, TZ, Location, DefaultShell, SkipExamples, Peer, BaseEnv) properly referenced.


248-256: LGTM! Path test uses Core.BaseEnv.

The assertion correctly accesses cfg.Core.BaseEnv.AsSlice() instead of the old Global path.


355-370: LGTM! YAML test expectations updated.

The expected Core struct is correctly populated with all fields including the Peer configuration.


540-591: LGTM! Error case tests use new helper.

All error-expecting tests now use testLoadWithError, which provides cleaner test code and consistent error handling.


789-800: LGTM! TestBindEnv_AsPath uses explicit loader.

The test now creates a ConfigLoader instance and calls bindEnvironmentVariables directly, aligning with the new explicit Viper instance pattern.

internal/common/config/loader.go (5)

960-967: Good use of strings.Cut for parsing.

The refactor from manual string splitting to strings.Cut is cleaner and more robust. The handling of empty values and whitespace trimming is appropriate.


188-242: Well-structured service-aware configuration loading.

The buildConfig method cleanly separates core/paths loading (always needed) from service-specific sections, with proper validation gating. The modular approach improves maintainability.


112-119: Correct bit-flag checking with sensible fallback.

The requires method properly uses bitwise AND for section checking and safely falls back to SectionAll for unknown services.


131-186: Clean configuration loading flow with isolated viper instance.

The Load method properly uses the isolated viper instance (l.v) throughout, handles missing config files gracefully, and maintains backward compatibility with admin.yaml merging.


49-57: ConfigLoader struct with isolated viper instance is well-designed.

The introduction of an isolated viper instance (v *viper.Viper) in the ConfigLoader struct enables better testability and avoids global state issues. The service field enables selective configuration loading.

Comment on lines +431 to +437
// validateUIConfig validates the UI configuration.
func (l *ConfigLoader) validateUIConfig(cfg *Config) error {
if cfg.UI.MaxDashboardPageLimit < 1 {
return fmt.Errorf("invalid max dashboard page limit: %d", cfg.UI.MaxDashboardPageLimit)
}
return nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validation may fail for default configurations.

validateUIConfig requires MaxDashboardPageLimit >= 1, but loadUIConfig only sets this field when def.UI != nil. If the UI section is absent from the config file, cfg.UI.MaxDashboardPageLimit remains 0 (the zero value), causing validation to fail despite a viper default of 100 being set.

Consider setting the default directly on cfg.UI.MaxDashboardPageLimit before or after the if def.UI != nil block in loadUIConfig, or use l.v.GetInt("ui.maxDashboardPageLimit") to retrieve the defaulted value.

 // loadUIConfig loads the UI configuration.
 func (l *ConfigLoader) loadUIConfig(cfg *Config, def Definition) {
+	// Apply defaults from viper (these include the configured defaults)
+	cfg.UI.MaxDashboardPageLimit = l.v.GetInt("ui.maxDashboardPageLimit")
+	cfg.UI.NavbarTitle = l.v.GetString("ui.navbarTitle")
+	cfg.UI.LogEncodingCharset = l.v.GetString("ui.logEncodingCharset")
+
 	if def.UI != nil {
 		cfg.UI.NavbarColor = def.UI.NavbarColor
-		cfg.UI.NavbarTitle = def.UI.NavbarTitle
-		cfg.UI.MaxDashboardPageLimit = def.UI.MaxDashboardPageLimit
-		cfg.UI.LogEncodingCharset = def.UI.LogEncodingCharset
+		if def.UI.NavbarTitle != "" {
+			cfg.UI.NavbarTitle = def.UI.NavbarTitle
+		}
+		if def.UI.MaxDashboardPageLimit > 0 {
+			cfg.UI.MaxDashboardPageLimit = def.UI.MaxDashboardPageLimit
+		}
+		if def.UI.LogEncodingCharset != "" {
+			cfg.UI.LogEncodingCharset = def.UI.LogEncodingCharset
+		}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
internal/common/config/loader.go around lines 431-437: validateUIConfig fails
when cfg.UI is nil because cfg.UI.MaxDashboardPageLimit stays at the Go zero
value (0) even though viper has a default; fix loadUIConfig to ensure cfg.UI is
non-nil and assign the defaulted value into cfg.UI.MaxDashboardPageLimit (e.g.,
if cfg.UI == nil { cfg.UI = &UIConfig{} } and set cfg.UI.MaxDashboardPageLimit =
l.v.GetInt("ui.maxDashboardPageLimit") or set it after the def.UI block), so
validation no longer rejects configs that rely on the viper default.

Copy link

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

Caution

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

⚠️ Outside diff range comments (1)
internal/common/config/loader.go (1)

516-559: ZombieDetectionInterval default is skipped on invalid user values

In loadSchedulerConfig, the default for cfg.Scheduler.ZombieDetectionInterval is only applied when the value is zero and !l.v.IsSet("scheduler.zombieDetectionInterval"). If the user sets an invalid duration (parse error), IsSet will be true, the field will stay at its zero value, and the 45s default will never be applied, unlike the other scheduler durations which always fall back when <=0.

To make behavior consistent and more forgiving of invalid values, consider aligning this with the other scheduler fields, e.g.:

- if cfg.Scheduler.ZombieDetectionInterval == 0 && !l.v.IsSet("scheduler.zombieDetectionInterval") {
-   cfg.Scheduler.ZombieDetectionInterval = 45 * time.Second
- }
+ if cfg.Scheduler.ZombieDetectionInterval <= 0 {
+   cfg.Scheduler.ZombieDetectionInterval = 45 * time.Second
+ }

This way, parse failures or explicitly zero values both get a sane default instead of silently leaving the interval at 0.

🧹 Nitpick comments (2)
internal/common/config/loader.go (2)

18-47: Service/section modeling and gating look solid

The Service enum, ConfigSection bitflags, serviceRequirements map, and requires helper form a clean, understandable way to gate which sections load per service; comments match the actual masks, and defaulting unknown services to SectionAll is a safe fallback for CLI-style entrypoints.

One minor robustness tweak you might consider: guard against a nil *viper.Viper in NewConfigLoader (e.g., instantiate a new one or panic with a clear message), to avoid a potential nil dereference if a caller misuses the constructor.

Also applies to: 76-83, 85-120, 121-129


614-675: Legacy handling and env binding are well-scoped; minor consistency nit on path envs

The combination of LoadLegacyFields, loadLegacyEnv, envBindings, and bindEnvironmentVariables gives a clear, service-aware story for both legacy and current config inputs, and the default seeding in setViperDefaultValues matches how the section loaders consume values.

One small consistency gap: the legacy path env mappings (e.g. DAGU__DATA, DAGU__SUSPEND_FLAGS_DIR, DAGU__ADMIN_LOGS_DIR) in loadLegacyEnv assign raw strings into cfg.Paths.*, whereas the newer env bindings for file paths go through absolute-path normalization. If you want fully uniform behavior, you could optionally wrap those legacy setters with the same normalization (or reuse fileutil.ResolvePathOrBlank), while keeping the existing deprecation warnings.

Also applies to: 677-741, 743-760, 762-813, 815-928, 930-948, 950-962

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d648232 and e0cceed.

📒 Files selected for processing (2)
  • internal/common/config/loader.go (15 hunks)
  • internal/runtime/env.go (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/runtime/env.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/common/config/loader.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
🔇 Additional comments (4)
internal/common/config/loader.go (4)

131-186: Config loading pipeline is cohesive and service-aware

The LoadbuildConfig flow is well-structured: viper setup, config file/admin merge, unmarshal into Definition, then per-section loaders gated via requires(), followed by legacy handling, path finalization, and targeted validation. Always loading core+paths while gating the rest aligns with the Service/ConfigSection model, and timezone initialization errors are surfaced clearly.

No functional issues stand out in this part.

Also applies to: 188-242, 244-275, 277-293


295-401: Server and auth configuration handling is thorough

The server loader and validator cover the main edge cases (permissions overrides, optional TLS, legacy vs. new auth settings, TTL parsing with warnings, OIDC auto-detection) and normalize BasePath via cleanServerBasePath in a predictable way. The validation checks are appropriate and avoid over-constraining the config.

No changes needed here from a correctness perspective.

Also applies to: 403-415, 990-1008


416-454: Section-specific loaders and defaults are generally well-behaved

UI, queues, coordinator, worker, monitoring, and path finalization logic all look consistent and robust. In particular, seeding cfg.UI from viper defaults before applying def.UI fixes the prior default/validation mismatch, and parseWorkerLabels plus the map-based cases give good flexibility for worker labels while ignoring malformed entries safely.

Also applies to: 456-472, 474-514, 516-559, 561-587, 589-612


964-987: Worker label parsing is robust

parseWorkerLabels’ use of trimming, skipping empty segments, and strings.Cut to only split on the first = yields predictable label maps and tolerates messy input without panicking or mis-parsing values containing =.

Looks good as-is.

@yottahmd
Copy link
Collaborator Author

yottahmd commented Dec 9, 2025

@coderabbitai generate docstrings

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link

coderabbitai bot commented Dec 9, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #1468

coderabbitai bot added a commit that referenced this pull request Dec 9, 2025
Docstrings generation was requested by @yottahmd.

* #1467 (comment)

The following files were modified:

* `internal/cmd/agent_executor.go`
* `internal/cmd/context.go`
* `internal/cmd/coordinator.go`
* `internal/cmd/dry.go`
* `internal/cmd/exec.go`
* `internal/cmd/flags.go`
* `internal/cmd/restart.go`
* `internal/cmd/retry.go`
* `internal/cmd/scheduler.go`
* `internal/cmd/start.go`
* `internal/common/config/context.go`
* `internal/common/config/env.go`
* `internal/common/config/loader.go`
* `internal/common/config/timzone.go`
* `internal/common/masking/masker.go`
* `internal/common/stringutil/kv.go`
* `internal/core/spec/variables.go`
* `internal/runtime/env.go`
* `internal/runtime/subcmd.go`
* `internal/service/frontend/server.go`
* `internal/service/scheduler/scheduler.go`
* `internal/test/helper.go`
coderabbitai bot and others added 2 commits December 9, 2025 23:14
Docstrings generation was requested by @yottahmd.

* #1467 (comment)

The following files were modified:

* `internal/cmd/agent_executor.go`
* `internal/cmd/context.go`
* `internal/cmd/coordinator.go`
* `internal/cmd/dry.go`
* `internal/cmd/exec.go`
* `internal/cmd/flags.go`
* `internal/cmd/restart.go`
* `internal/cmd/retry.go`
* `internal/cmd/scheduler.go`
* `internal/cmd/start.go`
* `internal/common/config/context.go`
* `internal/common/config/env.go`
* `internal/common/config/loader.go`
* `internal/common/config/timzone.go`
* `internal/common/masking/masker.go`
* `internal/common/stringutil/kv.go`
* `internal/core/spec/variables.go`
* `internal/runtime/env.go`
* `internal/runtime/subcmd.go`
* `internal/service/frontend/server.go`
* `internal/service/scheduler/scheduler.go`
* `internal/test/helper.go`

<details>
<summary>These files were ignored</summary>

* `internal/cmd/agent_executor_test.go`
* `internal/common/config/context_test.go`
* `internal/common/config/env_test.go`
* `internal/common/config/loader_test.go`
* `internal/common/config/timezone_test.go`
* `internal/core/spec/builder_test.go`
* `internal/integration/base_dag_test.go`
* `internal/integration/parallel_test.go`
* `internal/runtime/env_test.go`
* `internal/runtime/subcmd_test.go`
* `internal/service/worker/handler_test.go`

</details>

<details>
<summary>ℹ️ Note</summary><blockquote>

CodeRabbit cannot perform edits on its own pull requests yet.

</blockquote></details>

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
internal/runtime/env.go (1)

71-71: Update comment to reflect actual precedence.

The comment states Step.Env > Envs > Variables > ..., implying Step.Env has the highest precedence. However, the implementation at lines 98-100 gives Step.Env the lowest precedence—it's only added if the key doesn't already exist. The comment on lines 89-92 correctly clarifies this is intentional to avoid overwriting evaluated values with raw placeholders.

Apply this diff to correct the precedence order:

-// Precedence: Step.Env > Envs > Variables > SecretEnvs > DAGContext.Envs > DAG.Env
+// Precedence: Envs > Variables > SecretEnvs > DAGContext.Envs > DAG.Env > Step.Env
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0cceed and b9fa844.

📒 Files selected for processing (2)
  • internal/common/config/loader.go (15 hunks)
  • internal/runtime/env.go (4 hunks)
🧰 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/env.go
  • internal/common/config/loader.go
🧬 Code graph analysis (2)
internal/runtime/env.go (1)
internal/core/step.go (1)
  • Step (13-77)
internal/common/config/loader.go (5)
internal/common/config/definition.go (1)
  • Definition (5-138)
internal/common/config/config.go (9)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
  • Server (84-121)
  • UI (207-213)
  • Coordinator (252-257)
  • Scheduler (267-283)
  • Auth (144-150)
  • AuthToken (177-179)
internal/common/config/env.go (1)
  • BaseEnv (9-11)
internal/common/config/path.go (1)
  • Paths (13-30)
internal/common/fileutil/fileutil.go (1)
  • ResolvePathOrBlank (187-194)
⏰ 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 (9)
internal/runtime/env.go (4)

77-80: LGTM: Clean migration to strings.Cut.

The use of strings.Cut is more idiomatic than strings.SplitN for parsing "key=value" strings. It clearly signals intent with the found boolean and handles edge cases correctly (empty values, values containing "=", missing separators).


94-101: LGTM: Correct use of strings.Cut with precedence check.

The migration to strings.Cut is correct, and the exists check (line 98) properly implements the lower precedence for Step.Env as clarified in the comments above.


120-123: LGTM: Consistent strings.Cut usage for DAG params.

The migration is correct. Storing the entire param string (line 122) is consistent with how Variables is used throughout the codebase—values are expected in "key=value" format (see line 63 and line 77).


393-396: LGTM: Clean strings.Cut migration.

The migration is correct and follows the same safe pattern as the other changes in this file.

internal/common/config/loader.go (5)

18-119: Excellent service-aware loading architecture!

The introduction of Service and ConfigSection types with the requirements mapping creates a clean, efficient system for loading only necessary configuration. The bit flag approach for ConfigSection is idiomatic and performant, and the requires() method provides a safe API with sensible fallback to SectionAll for unknown services.

The architecture correctly handles Core and Paths as always-loaded sections (confirmed in buildConfig lines 193-197), while other sections load conditionally based on service requirements.


188-242: Well-structured configuration pipeline!

The refactored buildConfig method implements a clear, maintainable pipeline:

  1. Always loads Core and Paths (required by all services)
  2. Conditionally loads service-specific sections using the requires() API
  3. Applies legacy field and environment overrides
  4. Finalizes derived paths
  5. Validates only the loaded sections

This modular approach improves both performance and code clarity.


416-454: UI config loading correctly fixed!

The refactored loadUIConfig now properly handles the case where the UI section is absent from the config file:

  1. Lines 418-423 explicitly retrieve viper defaults BEFORE checking def.UI
  2. Lines 427-443 only override defaults when explicit values are provided
  3. Validation at line 450 now passes even when the UI section is absent, as MaxDashboardPageLimit correctly defaults to 100 (set via viper on line 795)

This resolves the previous validation issue mentioned in past reviews.


933-951: Note: Path normalization mutates environment variables.

Lines 942-946 normalize relative paths in environment variables to absolute paths and update the environment variables using os.Setenv:

if b.isPath {
    if val := os.Getenv(fullEnv); val != "" {
        if abs, err := filepath.Abs(val); err == nil && abs != val {
            _ = os.Setenv(fullEnv, abs)
        }
    }
}

This ensures consistency but has the side effect that any code reading these environment variables after configuration loading will see the normalized absolute paths rather than the original values. This behavior is acceptable for internal configuration loading but is worth noting for maintainability.


967-991: Excellent use of strings.Cut for label parsing!

The refactored parseWorkerLabels uses strings.Cut instead of strings.SplitN, which is cleaner and more idiomatic:

key, value, found := strings.Cut(pair, "=")
if found {
    key = strings.TrimSpace(key)
    value = strings.TrimSpace(value)
    if key != "" {
        labels[key] = value
    }
}

This correctly handles all edge cases:

  • "key=value"labels["key"] = "value"
  • "key="labels["key"] = "" (allows empty values)
  • "key" (no =) → skipped
  • "=value" → skipped (empty key rejected)
  • Whitespace trimmed appropriately

The implementation is robust and aligns with the PR objectives of standardizing to strings.Cut.

Copy link

@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

Caution

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

⚠️ Outside diff range comments (1)
internal/core/spec/variables.go (1)

56-60: Critical: Duplicate parsing of map[string]any entries.

This block is redundant and causes a bug. The switch statement at lines 42-45 already handles map[string]any entries by calling parseKeyValue. This post-switch if block then checks the same condition and calls parseKeyValue again, resulting in every map entry's key-value pairs being added twice to the pairs slice.

This will cause duplicate environment variables to be processed.

Remove the redundant block:

 			pairs = append(pairs, pair{key: key, val: val})
 		default:
 			return nil, core.NewValidationError("env", &pairs, fmt.Errorf("%w: %s", ErrInvalidEnvValue, v))
 		}
-		if aa, ok := v.(map[string]any); ok {
-			if err := parseKeyValue(aa, &pairs); err != nil {
-				return nil, core.NewValidationError("env", v, err)
-			}
-		}
 	}
🧹 Nitpick comments (6)
internal/cmd/context.go (2)

76-78: Docstring has redundant opening line.

Line 76 repeats the function name and purpose already stated on line 75. The multi-line docstring should flow as a single coherent description.

 // NewContext initializes the application setup by loading configuration,
-// NewContext creates and initializes an application Context for the given Cobra command.
-// It binds command flags, loads configuration scoped to the command, configures logging (respecting debug, quiet, and log format settings), logs any configuration warnings, and initializes history, DAG run, proc, queue, and service registry stores and managers used by the application.
-// NewContext returns an initialized Context or an error if flag retrieval, configuration loading, or other initialization steps fail.
+// binding command flags, loading configuration scoped to the command, configuring logging
+// (respecting debug, quiet, and log format settings), logging any configuration warnings,
+// and initializing history, DAG run, proc, queue, and service registry stores and managers.
+// It returns an initialized Context or an error if any initialization step fails.

183-204: Docstring is redundant and verbose.

Lines 183-187 repeat the function name and duplicate what the code already clearly expresses. Consider a concise one-liner docstring.

-// serviceForCommand returns the appropriate config.Service type for a given command name.
-// serviceForCommand determines which config.Service to load for a given command name.
-// "server" -> ServiceServer, "scheduler" -> ServiceScheduler, "worker" -> ServiceWorker,
-// "coordinator" -> ServiceCoordinator, and "start", "restart", "retry", "dry", "exec" -> ServiceAgent.
-// For any other command it returns ServiceNone so all configuration sections are loaded.
+// serviceForCommand maps a command name to its required config.Service type.
 func serviceForCommand(cmdName string) config.Service {
internal/test/helper.go (2)

78-80: Docstring is verbose; consider trimming.

The Setup docstring spans multiple detailed sentences. A shorter summary with key points would improve readability.


229-232: Docstring is overly detailed.

Consider a one-liner: // writeHelperConfigFile writes a minimal YAML config for subprocess use.

internal/common/config/loader.go (2)

77-84: Incomplete docstring on line 79.

Line 79 appears truncated: // ConfigLoader to determine which configuration sections to load. This seems like a leftover from editing.

 // WithService sets the service type for configuration loading.
 // This allows the loader to only process configuration sections
-// ConfigLoader to determine which configuration sections to load.
+// relevant to the specific service.
 func WithService(service Service) ConfigLoaderOption {

122-124: Incomplete docstring on line 123.

Line 123 is truncated: // ConfigLoaderOption values (for example: service, config file path, or app home directory).

 // NewConfigLoader creates a new ConfigLoader instance with an isolated viper instance
-// ConfigLoaderOption values (for example: service, config file path, or app home directory).
+// and applies the given options (e.g., service type, config file path, app home directory).
 func NewConfigLoader(viper *viper.Viper, options ...ConfigLoaderOption) *ConfigLoader {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9fa844 and 38551ca.

📒 Files selected for processing (22)
  • internal/cmd/agent_executor.go (1 hunks)
  • internal/cmd/context.go (8 hunks)
  • internal/cmd/coordinator.go (2 hunks)
  • internal/cmd/dry.go (2 hunks)
  • internal/cmd/exec.go (2 hunks)
  • internal/cmd/flags.go (2 hunks)
  • internal/cmd/restart.go (2 hunks)
  • internal/cmd/retry.go (2 hunks)
  • internal/cmd/scheduler.go (1 hunks)
  • internal/cmd/start.go (2 hunks)
  • internal/common/config/context.go (1 hunks)
  • internal/common/config/env.go (1 hunks)
  • internal/common/config/loader.go (15 hunks)
  • internal/common/config/timzone.go (1 hunks)
  • internal/common/masking/masker.go (1 hunks)
  • internal/common/stringutil/kv.go (2 hunks)
  • internal/core/spec/variables.go (2 hunks)
  • internal/runtime/env.go (4 hunks)
  • internal/runtime/subcmd.go (1 hunks)
  • internal/service/frontend/server.go (3 hunks)
  • internal/service/scheduler/scheduler.go (2 hunks)
  • internal/test/helper.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • internal/service/scheduler/scheduler.go
  • internal/common/config/timzone.go
  • internal/runtime/subcmd.go
  • internal/service/frontend/server.go
  • internal/common/config/context.go
  • internal/cmd/dry.go
  • internal/common/config/env.go
  • internal/cmd/scheduler.go
  • internal/runtime/env.go
  • internal/cmd/restart.go
  • internal/cmd/coordinator.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/common/masking/masker.go
  • internal/cmd/flags.go
  • internal/test/helper.go
  • internal/cmd/start.go
  • internal/cmd/exec.go
  • internal/cmd/context.go
  • internal/common/stringutil/kv.go
  • internal/common/config/loader.go
  • internal/cmd/retry.go
  • internal/core/spec/variables.go
  • internal/cmd/agent_executor.go
🧠 Learnings (2)
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*.go : Keep Go files `gofmt`/`goimports` clean; use tabs, PascalCase for exported symbols (`SchedulerClient`), lowerCamelCase for locals, and `Err...` names for package-level errors

Applied to files:

  • internal/test/helper.go
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*.go : Backend entrypoint in `cmd/` orchestrates the scheduler and CLI; runtime, persistence, and service layers sit under `internal/*` (for example `internal/runtime`, `internal/persistence`)

Applied to files:

  • internal/cmd/context.go
🧬 Code graph analysis (7)
internal/test/helper.go (2)
internal/common/config/loader.go (1)
  • NewConfigLoader (124-130)
internal/common/config/config.go (3)
  • Core (53-81)
  • Config (9-39)
  • Peer (286-301)
internal/cmd/start.go (1)
internal/common/config/config.go (2)
  • Core (53-81)
  • Peer (286-301)
internal/cmd/context.go (3)
internal/common/config/config.go (3)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
internal/common/logger/logger.go (3)
  • Config (45-50)
  • WithFormat (62-66)
  • Option (52-52)
internal/common/config/loader.go (3)
  • WithService (80-84)
  • NewConfigLoader (124-130)
  • Service (21-21)
internal/common/config/loader.go (5)
internal/common/config/definition.go (1)
  • Definition (5-138)
internal/common/config/config.go (9)
  • Config (9-39)
  • Core (53-81)
  • Peer (286-301)
  • Server (84-121)
  • UI (207-213)
  • Coordinator (252-257)
  • Scheduler (267-283)
  • Auth (144-150)
  • AuthToken (177-179)
internal/common/config/env.go (2)
  • LoadBaseEnv (31-33)
  • BaseEnv (9-11)
internal/common/config/path.go (1)
  • Paths (13-30)
internal/common/fileutil/fileutil.go (1)
  • ResolvePathOrBlank (187-194)
internal/cmd/retry.go (1)
internal/common/config/config.go (2)
  • Core (53-81)
  • Peer (286-301)
internal/core/spec/variables.go (1)
internal/core/spec/errors.go (1)
  • ErrInvalidEnvValue (27-27)
internal/cmd/agent_executor.go (3)
internal/cmd/context.go (1)
  • Context (40-55)
internal/common/logger/logger.go (5)
  • Option (52-52)
  • Config (45-50)
  • WithDebug (55-59)
  • WithQuiet (76-80)
  • WithFormat (62-66)
internal/common/config/config.go (2)
  • Config (9-39)
  • Core (53-81)
⏰ 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 (31)
internal/core/spec/variables.go (2)

16-26: LGTM! Comprehensive docstring.

The updated docstring clearly documents the function's parameters, behavior (including command substitution and variable expansion), and error conditions.


48-52: LGTM! Clean migration to strings.Cut.

The change from strings.SplitN to strings.Cut is correct and more idiomatic. The found boolean properly indicates whether the separator was present.

internal/common/masking/masker.go (1)

74-82: LGTM! Clean migration to strings.Cut.

The refactored splitEnv function correctly uses strings.Cut to split on the first '=' and safely handles entries without '=' by returning two empty strings. The caller at line 27 properly handles this by skipping empty values at line 30.

internal/common/stringutil/kv.go (2)

17-20: LGTM! Correct use of strings.Cut.

The Key() method correctly uses strings.Cut to extract the key portion before the first '='.


22-28: LGTM! Proper handling of entries without '='.

The Value() method correctly uses strings.Cut with the found flag to return an empty string when '=' is not present.

internal/cmd/agent_executor.go (1)

72-78: LGTM!

The migration from ctx.Config.Global.Debug and ctx.Config.Global.LogFormat to ctx.Config.Core.Debug and ctx.Config.Core.LogFormat is consistent with the Core struct definition. The logic remains unchanged.

internal/cmd/start.go (2)

460-469: Well-documented function.

The new docstring for executeDAGRun is comprehensive—it clearly describes the initialization flow, log file handling, DAG store setup, agent construction, and error scenarios.


500-500: LGTM!

The change from ctx.Config.Global.Peer to ctx.Config.Core.Peer is consistent with the Core struct definition and the overall config migration in this PR.

internal/cmd/exec.go (2)

65-72: Well-documented function.

The new docstring for runExec comprehensively describes the function's responsibilities including flag parsing, validation, DAG building, and execution/enqueueing logic.


173-178: LGTM! Improved parsing with strings.Cut.

Using strings.Cut is more idiomatic for Go 1.18+ and correctly handles edge cases:

  • Missing = separator
  • Empty key (e.g., =value)
  • Empty value (e.g., key=)
internal/cmd/retry.go (2)

140-143: Well-documented function.

The docstring for executeRetry clearly describes the setup steps (log file, environment, DAG store, agent configuration) and the delegation to the shared executor.


180-180: LGTM!

The change from ctx.Config.Global.Peer to ctx.Config.Core.Peer is consistent with the Core struct definition and aligns with the same change in start.go and other files.

internal/cmd/flags.go (2)

279-295: LGTM!

The initFlags function is well-structured with clear separation of concerns:

  • Always includes base flags (config, dagu-home, quiet, cpu-profile)
  • Properly handles boolean vs string flag registration
  • Marks required flags appropriately

297-311: LGTM! Thread-safe explicit Viper binding.

The updated bindFlags function:

  • Takes an explicit *viper.Viper instance for better testability and per-service config isolation
  • Uses config.WithViperLock for thread-safe registration
  • Converts kebab-case flag names to camelCase keys via stringutil.KebabToCamel
  • Only binds flags with bindViper: true, which is the correct subset
internal/cmd/context.go (4)

82-83: Good use of isolated viper instance per context.

Creating a dedicated viper instance and binding flags explicitly avoids global state pollution across commands. This aligns well with the refactor goals.


60-68: Core field migration looks correct.

References to c.Config.Core.Debug and c.Config.Core.LogFormat align with the new Core-based configuration structure.


236-240: Coordinator client config correctly uses Core.Peer.

The migration from Global.Peer to Core.Peer for TLS/connection settings is consistent with the broader refactor.


188-204: Mapping is intentionally partial with a safe fallback.

The switch statement explicitly maps performance-critical services: server, scheduler, worker, coordinator, and agent commands (start, restart, retry, dry, exec). All other commands—including stop, status, start-all, migrate, enqueue, dequeue, and version—fall back to ServiceNone, which loads all config sections. This is functionally correct and safe; commands get everything they might need rather than failing on missing configuration.

If optimization is desired later, commands like status, stop, and version could benefit from lighter service profiles (e.g., ServiceMinimal skipping Queues). However, this is not necessary; the current approach is straightforward and maintainable.

internal/test/helper.go (5)

132-134: Correct use of isolated viper instance in tests.

Creating a fresh viper instance per test avoids cross-test contamination, mirroring the production pattern in NewContext.


137-139: Core field assignments are correct.

Setting cfg.Core.TZ, cfg.Core.Location, and cfg.Core.TzOffsetInSec aligns with the Core-based config structure.


237-243: Core-based field references in config file writer are correct.

The fields cfg.Core.Debug, cfg.Core.LogFormat, cfg.Core.DefaultShell, and cfg.Core.TZ are correctly sourced from the Core struct.


510-512: Good use of strings.Cut for key=value parsing.

Using strings.Cut with a presence check (found) is cleaner and more idiomatic than strings.SplitN with length checks.


572-572: Agent construction correctly uses Core.Peer.

Passing d.Config.Core.Peer aligns with the Core-based configuration migration.

internal/common/config/loader.go (8)

18-47: Service type and constants are well-documented.

The Service enum with clear docstrings for each service type improves code clarity. The comments now correctly reference "Core" instead of "Global".


89-101: Bit flag comments are now correct.

The inline comments (// 2, // 4, etc.) now match the actual values after iota increment from SectionNone = 0. Previous review feedback has been addressed.


417-455: UI config default loading issue addressed.

The loadUIConfig now retrieves defaults via l.v.GetInt("ui.maxDashboardPageLimit") and similar calls before applying def.UI overrides. This addresses the previous review concern about validation failing when the UI section is absent.


557-562: ZombieDetectionInterval silent-disable behavior remains.

Per the past review comment: if a user provides an invalid duration string (e.g., "invalid"), parsing fails, a warning is logged, but IsSet() returns true, so the default (45s) is not applied. This silently disables zombie detection rather than failing validation.

The current behavior is documented in comments (lines 557-559), but consider whether this is the intended UX—invalid config silently disabling a feature could be surprising. The alternative is to return a validation error for unparseable durations.

Is the current "warn but disable" behavior intentional for zombieDetectionInterval? If so, document this in user-facing config documentation. If not, consider returning an error on parse failure to prevent misconfiguration.


819-824: envBinding struct is clear and well-documented.

The declarative binding approach with isPath flag for path normalization is clean and maintainable.


934-952: Environment variable binding with path normalization is correct.

The loop correctly applies path normalization before binding and uses the loader's isolated viper instance.


968-994: parseWorkerLabels uses strings.Cut correctly.

The function properly handles the key=value format, trims whitespace, and skips empty keys. The use of strings.Cut is more idiomatic than SplitN.


860-863: Duplicate environment bindings for legacy UI keys.

Lines 860-863 duplicate bindings for ui.maxDashboardPageLimit, ui.logEncodingCharset, ui.navbarColor, and ui.navbarTitle with legacy env var names. This is intentional for backward compatibility, but note that viper will use the last bound env var if multiple are set simultaneously. Document this precedence if it matters.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
internal/common/stringutil/kv.go (1)

11-16: Implement the documented validation: panic when key contains '='.

The documentation on line 12 states "The key must not contain '=' characters" but the implementation doesn't enforce this constraint. The test in kv_test.go (lines 32-40) expects a panic for this case, which will cause test failures.

Apply this diff to add the validation:

 func NewKeyValue(key, value string) KeyValue {
+	if strings.Contains(key, "=") {
+		panic("stringutil: NewKeyValue: key must not contain '=' character")
+	}
 	return KeyValue(key + "=" + value)
 }

This aligns with the documented contract and ensures round-trip correctness with Key() and Value().

🧹 Nitpick comments (1)
internal/common/config/loader.go (1)

77-84: Minor comment wording issue.

The comment on lines 78-79 appears to have duplicated or fragmented text: "This allows the loader to only process configuration sections / ConfigLoader to determine which configuration sections to load." Consider clarifying:

Apply this diff to improve clarity:

-// WithService sets the service type for configuration loading.
-// This allows the loader to only process configuration sections
-// ConfigLoader to determine which configuration sections to load.
+// WithService sets the service type for configuration loading,
+// allowing the loader to only process configuration sections required by that service.
 func WithService(service Service) ConfigLoaderOption {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38551ca and 3b1d20e.

📒 Files selected for processing (4)
  • internal/cmd/agent_executor.go (1 hunks)
  • internal/common/config/loader.go (15 hunks)
  • internal/common/stringutil/kv.go (2 hunks)
  • internal/common/stringutil/kv_test.go (7 hunks)
🧰 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/common/stringutil/kv_test.go
  • internal/common/stringutil/kv.go
  • internal/common/config/loader.go
  • internal/cmd/agent_executor.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/common/stringutil/kv_test.go
🧠 Learnings (3)
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*_test.go : Co-locate Go tests as `*_test.go`; favour table-driven cases and cover failure paths

Applied to files:

  • internal/common/stringutil/kv_test.go
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: Applies to **/*_test.go : Use `stretchr/testify/require` and shared fixtures from `internal/test` instead of duplicating mocks

Applied to files:

  • internal/common/stringutil/kv_test.go
📚 Learning: 2025-12-04T10:34:17.051Z
Learnt from: CR
Repo: dagu-org/dagu PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-04T10:34:17.051Z
Learning: `make test` (or `make test-coverage`) executes the Go suite via `gotestsum`; append `TEST_TARGET=./internal/...` to focus packages

Applied to files:

  • internal/common/stringutil/kv_test.go
🧬 Code graph analysis (3)
internal/common/stringutil/kv_test.go (1)
internal/common/stringutil/kv.go (1)
  • NewKeyValue (14-16)
internal/common/stringutil/kv.go (1)
internal/common/logger/tag/tag.go (2)
  • Key (369-371)
  • Value (364-366)
internal/cmd/agent_executor.go (3)
internal/cmd/context.go (1)
  • Context (40-55)
internal/common/logger/logger.go (5)
  • Option (52-52)
  • Config (45-50)
  • WithDebug (55-59)
  • WithQuiet (76-80)
  • WithFormat (62-66)
internal/common/config/config.go (2)
  • Config (9-39)
  • Core (53-81)
🪛 GitHub Check: Go Linter
internal/common/stringutil/kv_test.go

[failure] 39-39:
SA4017: NewKeyValue doesn't have side effects and its return value is ignored (staticcheck)

⏰ 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 (21)
internal/common/stringutil/kv_test.go (3)

11-11: LGTM! Parallelization enhances test performance.

The addition of t.Parallel() to the test functions and subtests follows Go testing best practices and will improve test execution speed.

As per coding guidelines, table-driven tests with parallel execution are encouraged.

Also applies to: 14-14, 22-22, 33-33, 43-43, 129-129, 147-147, 183-183, 209-209, 219-219, 303-303


21-30: LGTM! Test correctly validates multi-equals handling in values.

This test case properly verifies that when a value contains '=' characters, Key() and Value() correctly split only on the first '=' separator.


42-53: LGTM! Round-trip test validates correctness.

This test confirms that a key-value pair constructed with NewKeyValue can be correctly retrieved via Key() and Value() methods, ensuring round-trip integrity.

internal/common/stringutil/kv.go (3)

18-21: LGTM! Cut-based parsing simplifies Key extraction.

The refactoring from strings.SplitN to strings.Cut correctly extracts the key (substring before the first '='). For entries without '=', it returns the entire string, which matches the documented behavior.


23-29: LGTM! Cut-based parsing handles multi-equals correctly.

The strings.Cut approach properly extracts everything after the first '=' as the value, correctly handling cases where the value itself contains '=' characters (e.g., "foo=bar=baz" returns "bar=baz").


56-69: LGTM! Clear documentation and correct Cut-based implementation.

The updated documentation (lines 56-59) accurately describes the behavior:

  • Splits at first '='
  • Skips entries without '='
  • Allows empty values and empty keys

The implementation correctly uses strings.Cut and the found return value to skip invalid entries.

internal/cmd/agent_executor.go (2)

69-70: Comment properly updated.

The function comment now correctly describes the purpose and behavior of configureLoggerForProgress. Good improvement from the previous truncated version.


73-73: LGTM: Refactored to use Core configuration.

The changes correctly update the configuration access from Global to Core, aligning with the PR's refactoring objectives.

Also applies to: 77-78

internal/common/config/loader.go (13)

18-47: LGTM: Well-documented service-aware configuration design.

The service type enumeration and associated documentation clearly explain which configuration sections each service requires. This selective loading approach should improve performance and reduce unnecessary processing.


86-101: LGTM: Bit flag values correctly documented.

The inline comments now accurately reflect the computed bit flag values. The use of bit flags for configuration sections is appropriate for efficient OR-based membership checks.


103-120: LGTM: Service requirements correctly implemented.

The serviceRequirements map and requires() method correctly implement the service-to-section membership logic using bit flags. The fallback to SectionAll for unknown services (line 117) is a safe default.


189-243: LGTM: Well-structured configuration building workflow.

The buildConfig method correctly orchestrates configuration loading with appropriate service-aware gating. The sequence—core/paths → service-specific sections → legacy overrides → finalization → validation—is logical and ensures only necessary sections are processed.


245-276: LGTM: Core configuration loaded correctly.

The function properly initializes the Core configuration, including base environment and peer settings. The timezone initialization with error handling is appropriate.


417-455: LGTM: UI configuration loading properly defaults and validates.

The addition of default value loading from viper (lines 419-424) before checking the definition resolves the previous concern about validation failures when the UI section is absent from the config file. The viper defaults (set on line 795 to 100) ensure MaxDashboardPageLimit is never 0, allowing validation to pass.


617-678: LGTM: Service-aware legacy field handling.

The refactored LoadLegacyFields correctly applies service-aware gating, ensuring legacy fields are only processed for configuration sections that are being loaded. Path-related legacy fields are correctly applied unconditionally since paths are always loaded.


680-744: LGTM: Service-aware legacy environment variable handling.

The loadLegacyEnv function correctly implements service-aware gating for legacy environment variables. The use of SectionNone for universally applicable settings (like path fields) is a clear pattern.


967-993: LGTM: Improved label parsing with strings.Cut.

The refactor to use strings.Cut (line 982) instead of strings.SplitN is a good modernization. The function correctly handles malformed entries by skipping pairs without an = delimiter, and properly trims whitespace around keys and values.


592-615: LGTM: Path finalization correctly derives subdirectories.

The finalizePaths function properly sets derived paths relative to DataDir and handles the executable path with appropriate error handling.


765-816: LGTM: Comprehensive default values configured.

The setViperDefaultValues function properly initializes all configuration defaults using the loader's isolated viper instance. The default values are sensible and align with validation requirements.


933-951: LGTM: Environment variable binding with path normalization.

The bindEnvironmentVariables function correctly binds configuration keys to environment variables using the loader's viper instance. The automatic path normalization for path-type bindings (lines 941-946) is a useful feature.


746-765: LGTM: Viper setup correctly uses isolated instance.

Both setupViper and configureViper properly use the loader's isolated viper instance (l.v), ensuring configuration isolation per loader. The setup sequence is appropriate.

Also applies to: 953-965

@yottahmd yottahmd merged commit fa9e41b into main Dec 9, 2025
5 checks passed
@yottahmd yottahmd deleted the refactor-config branch December 9, 2025 15:31
@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 88.85135% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.88%. Comparing base (d4b8484) to head (59f72bd).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
internal/common/config/loader.go 94.97% 7 Missing and 3 partials ⚠️
internal/runtime/env.go 61.53% 4 Missing and 1 partial ⚠️
internal/runtime/runner.go 0.00% 4 Missing ⚠️
internal/cmd/context.go 90.62% 2 Missing and 1 partial ⚠️
internal/common/collections/syncmap.go 0.00% 3 Missing ⚠️
internal/core/dag.go 0.00% 3 Missing ⚠️
internal/core/spec/variables.go 0.00% 3 Missing ⚠️
internal/cmd/exec.go 66.66% 0 Missing and 1 partial ⚠️
internal/common/config/env.go 50.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1467      +/-   ##
==========================================
- Coverage   59.92%   59.88%   -0.04%     
==========================================
  Files         196      196              
  Lines       21896    21892       -4     
==========================================
- Hits        13121    13110      -11     
- Misses       7367     7375       +8     
+ Partials     1408     1407       -1     
Files with missing lines Coverage Δ
internal/cmd/agent_executor.go 54.76% <100.00%> (ø)
internal/cmd/coordinator.go 72.07% <100.00%> (ø)
internal/cmd/dry.go 76.47% <100.00%> (ø)
internal/cmd/flags.go 100.00% <100.00%> (ø)
internal/cmd/restart.go 59.16% <100.00%> (ø)
internal/cmd/retry.go 49.24% <100.00%> (ø)
internal/cmd/scheduler.go 77.41% <100.00%> (ø)
internal/cmd/start.go 46.15% <100.00%> (ø)
internal/common/config/config.go 91.30% <ø> (ø)
internal/common/config/context.go 86.66% <100.00%> (ø)
... and 16 more

... and 2 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 d4b8484...59f72bd. 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