Skip to content

refactor: decouple DAGStore from the loader and derive the rebuild restore set - #2527

Merged
yohamta0 merged 6 commits into
mainfrom
refactor/dagstore-load-options
Aug 8, 2026
Merged

yohamta0 merged 6 commits into
mainfrom
refactor/dagstore-load-options

Conversation

@yohamta0

@yohamta0 yohamta0 commented Aug 7, 2026 •

Copy link
Copy Markdown
Member

Two independent simplifications, both behaviour-preserving.


1. Give DAGStore its own load-option vocabulary

DAGStore is a port interface, but two of its methods were parameterised by the implementation's option type:

GetDetails(ctx context.Context, fileName string, opts ...spec.LoadOption) (*core.DAG, error)
LoadSpec(ctx context.Context, spec []byte, opts ...spec.LoadOption) (*core.DAG, error)

That single import at internal/core/exec/dag.go put the 46k-line YAML loader into the transitive closure of everything importing internal/core/exec — every runtime/builtin/* executor, internal/runtime, runtime/executor, runtime/transform, most of persis/file. None of them touch a DAG store; they import exec to name a DAGRunStatus or a Node.

Give the port the vocabulary it needs and let the adapter translate:

// internal/core/exec — no spec import
type DAGLoadOptions struct {
	AllowBuildErrors bool
}

GetDetails(ctx context.Context, fileName string, opts DAGLoadOptions) (*core.DAG, error)
LoadSpec(ctx context.Context, source []byte, name string, opts DAGLoadOptions) (*core.DAG, error)

The surface is smaller than it looks. Across the whole codebase these methods were called with three of the eleven loader options, and one was already redundant: both implementations append spec.WithoutEval() unconditionally and no WithEval() exists to undo it, so the single caller passing it changed nothing. That leaves one real option, plus the name — which every LoadSpec caller supplied and which is now an explicit parameter rather than a field meaningful to only one of the two methods.

Result

packages compiling against internal/core/spec:  85 → 27   (58 freed)

Out of exec's closure entirely: core/spec (15,734 LOC), core/spec/types (1,648), llm (1,199), cmn/templatefuncs (157). No code is deleted — those still ship in the binary. What changes is how much of the tree recompiles when the loader changes, and that a port no longer depends on its adapter. Four test doubles also stop importing spec.

WithName("") sets the zero value, so applying it unconditionally is equivalent to omitting it.


2. Derive the rebuild restore set from the DAG's own tags

A DAG decoded from dag.json is incomplete by design: the fields tagged json:"-" are deliberately never written to disk. Retry and restart reload the stored YAML and copy those fields back — and that copy was a hand-written list of names that had to track the struct tags manually.

When it didn't, a field was restored on one path and dropped on another. That is exactly how S3, Redis, Kubernetes and WorkingDirExplicit came to be lost on API-initiated retries (#2521), and how PresolvedBuildEnv was discarded (#2522).

RestoreUnpersistedFrom derives the set instead. Every omitted field is restored; the three recording the outcome of a build rather than configuration are named as exclusions:

var buildOutcomeFields = map[string]bool{
	"EnvEvaluated":  true,
	"BuildErrors":   true,
	"BuildWarnings": true,
}

The failure mode inverts. Adding a new omitted field to DAG now joins the restore automatically, so forgetting means the value is kept, not silently dropped.

Verified the derived set is exactly the twelve names the list held:

IDENTICAL: 12 fields [Env Harness Harnesses Kubernetes Params ParamsJSON
                      Redis RegistryAuths S3 SMTP SSH WorkingDirExplicit]

Env is the one field merged rather than replaced, so it is resolved after the wholesale copy.


Verification

  • go build ./... and go vet ./... clean
  • -race green: core, core/exec, core/spec, persis/file/dag, cmn/telemetry, runtime/agent, service/notification, cmd, service/frontend/api/v1
  • golangci-lint run ./... — 0 issues, and 0 under GOOS=windows
  • gofmt clean

Summary by CodeRabbit

  • New Features

    • Added an option to load DAGs even when build errors are present, including partial DAG results.
    • DAG loading now preserves explicitly provided DAG names.
  • Bug Fixes

    • Improved preservation of DAG settings and metadata when rebuilding from YAML.
    • Build results and warnings are handled separately and are not unintentionally restored.
    • Improved consistency when loading DAGs across scheduling, runtime, webhook, notification, and frontend workflows.

DAGStore declared two methods taking spec.LoadOption, so the port
interface was parameterised by its implementation's option type. Every
consumer of internal/core/exec therefore compiled against the YAML
loader, whether or not it touched a DAG store at all.

Define the options the port actually needs and let the file-backed store
translate them. Across the whole codebase these two methods were called
with three of the eleven available loader options, and one of those three
was redundant: both implementations already append WithoutEval
unconditionally, so a caller passing it changed nothing. What remains is
whether build errors are tolerated, plus the name LoadSpec builds under,
which every caller supplied.

This takes internal/core/spec, internal/core/spec/types, internal/llm and
internal/cmn/templatefuncs out of the transitive closure of everything
importing exec, cutting the packages that compile against the loader from
85 to 27. Four test doubles stop importing spec as well.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 38dac7ab-9511-44eb-8912-3bd21a99918d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR replaces variadic spec.LoadOption parameters with exec.DAGLoadOptions, adds explicit DAG names to LoadSpec, translates options in file storage, generalizes DAG field restoration, and updates call sites, mocks, and tests.

Changes

DAG loading and field restoration

Layer / File(s) Summary
Define DAG loading contracts
internal/core/exec/dag.go
Adds DAGLoadOptions with AllowBuildErrors. Updates DAGStore.GetDetails and LoadSpec signatures.
Translate options in file storage
internal/persis/file/dag/store.go, internal/persis/file/dag/store_test.go
Translates execution-layer options, preserves explicit DAG names during YAML loading, disables evaluation, and tests build-error handling.
Generalize unpersisted field restoration
internal/core/restore.go, internal/core/restore_test.go, internal/core/spec/rebuild.go
Adds reflective restoration for JSON-excluded DAG fields. RebuildFromYAML uses it and merges environment data separately. Tests verify preserved persisted and build-outcome fields.
Migrate DAG loading call sites
internal/runtime/agent/dbclient.go, internal/service/frontend/api/v1/dags.go, internal/service/frontend/api/v1/notifications.go, internal/service/frontend/api/v1/webhooks.go, internal/service/notification/service.go
Updates DAG lookup and loading calls. Frontend paths preserve AllowBuildErrors; DAG creation and updates pass explicit names.
Update mocks and integration coverage
internal/cmn/telemetry/collector_test.go, internal/runtime/agent/dbclient_test.go, internal/service/frontend/api/v1/dags_test.go, internal/service/frontend/api/v1/dagruns_test.go, internal/service/notification/service_test.go, internal/intg/*
Updates mock method signatures and test calls to use exec.DAGLoadOptions and the new LoadSpec arguments.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant FrontendAPI
  participant DAGStore
  participant FileStorage
  participant YAMLLoader
  FrontendAPI->>DAGStore: LoadSpec with name and DAGLoadOptions
  DAGStore->>FileStorage: Translate AllowBuildErrors
  FileStorage->>YAMLLoader: Load YAML with name and loader options
  YAMLLoader-->>FileStorage: DAG or partial DAG with build errors
  FileStorage-->>DAGStore: Return loaded DAG
  DAGStore-->>FrontendAPI: Return DAG result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes both primary changes: DAGStore decoupling and derived DAG restore fields.
Description check ✅ Passed The description thoroughly covers the summary, changes, rationale, and verification, although it does not follow every template heading.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/dagstore-load-options

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/persis/file/dag/store_test.go (1)

364-387: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Test GetDetails with AllowBuildErrors.

The test only uses exec.DAGLoadOptions{}. Add a DAG with a build error. Assert that GetDetails returns the partially built DAG when AllowBuildErrors is true. Assert the default path keeps the existing failure behavior.

As per coding guidelines, “Add or update tests appropriate to the changed code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/persis/file/dag/store_test.go` around lines 364 - 387, Extend the
GetDetails test to create a DAG with a build error, then verify the default
exec.DAGLoadOptions{} path preserves the existing error behavior. Also call
GetDetails with AllowBuildErrors enabled and assert it returns the partially
built DAG without failing.

Source: Coding guidelines

🧹 Nitpick comments (1)
internal/cmn/telemetry/collector_test.go (1)

98-99: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Keep the explicit LoadSpec name observable in both mocks.

Both mocks accept the new name parameter but omit it from m.Called. Tests cannot detect an incorrect DAG name.

  • internal/cmn/telemetry/collector_test.go#L98-L99: pass name to m.Called.
  • internal/runtime/agent/dbclient_test.go#L93-L94: pass name to m.Called.

As per coding guidelines, add or update tests appropriate to changed Go code.

Proposed fix
-func (m *mockDAGStore) LoadSpec(ctx context.Context, source []byte, _ string, opts exec.DAGLoadOptions) (*core.DAG, error) {
-	args := m.Called(ctx, source, opts)
+func (m *mockDAGStore) LoadSpec(ctx context.Context, source []byte, name string, opts exec.DAGLoadOptions) (*core.DAG, error) {
+	args := m.Called(ctx, source, name, opts)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cmn/telemetry/collector_test.go` around lines 98 - 99, Update
mockDAGStore.LoadSpec in internal/cmn/telemetry/collector_test.go at lines 98-99
to include the explicit name argument in m.Called, and make the same change in
internal/runtime/agent/dbclient_test.go at lines 93-94. Update or add relevant
tests so incorrect DAG names are detectable by both mocks.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/persis/file/dag/store_test.go`:
- Around line 1046-1053: Extend the LoadSpec test to pass a non-empty explicit
name different from the YAML name and assert dag.Name uses the explicit value.
Add a separate LoadSpec case using exec.DAGLoadOptions with AllowBuildErrors
enabled and assert the expected build-error behavior.

---

Outside diff comments:
In `@internal/persis/file/dag/store_test.go`:
- Around line 364-387: Extend the GetDetails test to create a DAG with a build
error, then verify the default exec.DAGLoadOptions{} path preserves the existing
error behavior. Also call GetDetails with AllowBuildErrors enabled and assert it
returns the partially built DAG without failing.

---

Nitpick comments:
In `@internal/cmn/telemetry/collector_test.go`:
- Around line 98-99: Update mockDAGStore.LoadSpec in
internal/cmn/telemetry/collector_test.go at lines 98-99 to include the explicit
name argument in m.Called, and make the same change in
internal/runtime/agent/dbclient_test.go at lines 93-94. Update or add relevant
tests so incorrect DAG names are detectable by both mocks.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4c863095-3194-4c9b-9948-991e1629e96e

📥 Commits

Reviewing files that changed from the base of the PR and between a334c61 and 24e2b68.

📒 Files selected for processing (15)
  • internal/cmn/telemetry/collector_test.go
  • internal/core/exec/dag.go
  • internal/intg/one_off_schedule_test.go
  • internal/intg/sched_test.go
  • internal/persis/file/dag/store.go
  • internal/persis/file/dag/store_test.go
  • internal/runtime/agent/dbclient.go
  • internal/runtime/agent/dbclient_test.go
  • internal/service/frontend/api/v1/dagruns_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_test.go
  • internal/service/frontend/api/v1/notifications.go
  • internal/service/frontend/api/v1/webhooks.go
  • internal/service/notification/service.go
  • internal/service/notification/service_test.go

Comment thread internal/persis/file/dag/store_test.go
Rebuilding a DAG from its source restored the JSON-excluded fields by
copying each one by name. The list had to track the json:"-" tags on DAG
by hand, and when it did not, a field was restored on one code path and
dropped on another: that is how S3, Redis, Kubernetes and the presolved
build env came to be lost on API-initiated retries.

Derive the set instead. Every omitted field is restored, and the three
that record the outcome of a build rather than configuration are named as
exclusions. Adding a new omitted field to DAG now joins the restore
automatically, so the default is to keep a value rather than silently
drop it.

The derived set is the same twelve fields the list named, so behaviour is
unchanged.
@yohamta0 yohamta0 changed the title refactor(exec): give DAGStore its own load-option vocabulary refactor: decouple DAGStore from the loader and derive the rebuild restore set Aug 7, 2026
@yohamta0

yohamta0 commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

LoadSpec now takes the DAG name as a parameter and a struct selecting
whether build errors are tolerated, but nothing exercised either: the
existing case passed an empty name and zero-value options.

Assert that an explicit name overrides the one the spec declares, and
that a spec which builds with errors fails unless the caller tolerates
them, in which case the partially built DAG comes back carrying them.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
internal/cmn/telemetry/collector_test.go (1)

98-99: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Record name in both LoadSpec mock calls.

The revised LoadSpec contract includes the DAG name, but both Testify mocks omit it from m.Called. Tests can therefore pass an incorrect name without failing.

  • internal/cmn/telemetry/collector_test.go#L98-L99: pass name to m.Called and update On("LoadSpec", ...) expectations.
  • internal/runtime/agent/dbclient_test.go#L93-L94: pass name to m.Called and update On("LoadSpec", ...) expectations.
Proposed mock fix
-func (m *mockDAGStore) LoadSpec(ctx context.Context, source []byte, _ string, opts exec.DAGLoadOptions) (*core.DAG, error) {
-	args := m.Called(ctx, source, opts)
+func (m *mockDAGStore) LoadSpec(ctx context.Context, source []byte, name string, opts exec.DAGLoadOptions) (*core.DAG, error) {
+	args := m.Called(ctx, source, name, opts)

As per coding guidelines, update tests for the changed Go contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cmn/telemetry/collector_test.go` around lines 98 - 99, Update
mockDAGStore.LoadSpec in internal/cmn/telemetry/collector_test.go at lines 98-99
to pass name to m.Called, and update every corresponding On("LoadSpec", ...)
expectation in that file. Apply the same change to the LoadSpec mock at
internal/runtime/agent/dbclient_test.go lines 93-94 and its expectations so both
test mocks validate the DAG name argument.

Source: Coding guidelines

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

53-54: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add coverage for explicit LoadSpec names.

LoadSpec now makes name part of the DAG identity contract. The supplied tests call it with an empty name, so they do not detect an adapter that drops or misapplies the explicit name. Add a case with name: yaml-name in the source and requested-name as the argument. Assert the intended returned core.DAG.Name.

The storage adapter in internal/persis/file/dag/store.go:339-344 translates this argument through spec.WithName(name). As per coding guidelines, add or update tests appropriate to the changed code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/exec/dag.go` around lines 53 - 54, Extend the LoadSpec coverage
to pass a non-empty requested name, such as “requested-name”, while the YAML
also contains “yaml-name”, then assert that the returned core.DAG.Name uses the
explicit argument. Add or update the storage-adapter test around
spec.WithName(name) to verify this name translation and preserve the
explicit-name precedence contract.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/cmn/telemetry/collector_test.go`:
- Around line 98-99: Update mockDAGStore.LoadSpec in
internal/cmn/telemetry/collector_test.go at lines 98-99 to pass name to
m.Called, and update every corresponding On("LoadSpec", ...) expectation in that
file. Apply the same change to the LoadSpec mock at
internal/runtime/agent/dbclient_test.go lines 93-94 and its expectations so both
test mocks validate the DAG name argument.

In `@internal/core/exec/dag.go`:
- Around line 53-54: Extend the LoadSpec coverage to pass a non-empty requested
name, such as “requested-name”, while the YAML also contains “yaml-name”, then
assert that the returned core.DAG.Name uses the explicit argument. Add or update
the storage-adapter test around spec.WithName(name) to verify this name
translation and preserve the explicit-name precedence contract.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ec9d5d8d-ba57-44ff-8d95-192d2cd31b6c

📥 Commits

Reviewing files that changed from the base of the PR and between a334c61 and 4e33e31.

📒 Files selected for processing (17)
  • internal/cmn/telemetry/collector_test.go
  • internal/core/exec/dag.go
  • internal/core/restore.go
  • internal/core/spec/rebuild.go
  • internal/intg/one_off_schedule_test.go
  • internal/intg/sched_test.go
  • internal/persis/file/dag/store.go
  • internal/persis/file/dag/store_test.go
  • internal/runtime/agent/dbclient.go
  • internal/runtime/agent/dbclient_test.go
  • internal/service/frontend/api/v1/dagruns_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_test.go
  • internal/service/frontend/api/v1/notifications.go
  • internal/service/frontend/api/v1/webhooks.go
  • internal/service/notification/service.go
  • internal/service/notification/service_test.go

@yohamta0

yohamta0 commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@yohamta0

yohamta0 commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai both nitpicks declined — one is already done, the other rests on a premise that does not hold. Evidence for each.

❌ "Add coverage for explicit LoadSpec names" — already landed

This review covered a334c61cac..4e33e31813, which predates the commit that added exactly this test. From the review's own commit range:

Reviewing files that changed from the base of the PR and between a334c61… and 4e33e31…

17138889f5 added it at internal/persis/file/dag/store_test.go:1057-1059:

dag, err = store.LoadSpec(ctx, []byte(validSpec), "explicit-name", exec.DAGLoadOptions{})
require.NoError(t, err)
assert.Equal(t, "explicit-name", dag.Name)

along with the build-error case, in response to the earlier Major finding on this same PR. Nothing to do.

❌ "Record name in both LoadSpec mock calls" — the mocks are never invoked

Tests can therefore pass an incorrect name without failing.

No test passes any name, because LoadSpec is never called on either mock. Across both packages:

internal/cmn/telemetry/collector_test.go   On("LoadSpec") = 0   .LoadSpec( = 0
internal/runtime/agent/dbclient_test.go    On("LoadSpec") = 0   .LoadSpec( = 0

and a package-wide search for LoadSpec outside the two method declarations returns nothing. These methods exist solely to satisfy exec.DAGStore; the m.Called bodies are unreachable.

So threading name through would change no observable behaviour, and there are no On("LoadSpec", ...) expectations to update — the diff would be churn against dead code. The _ string parameter is also the more honest signature here: it states that this mock ignores the name, rather than implying a recorded argument nobody asserts on.

If a test ever does exercise LoadSpec through these mocks, recording the argument should land with that test, where it can actually fail.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@yohamta0, thank you for the detailed evidence.

I withdraw both nitpicks.

internal/persis/file/dag/store_test.go already covers the explicit LoadSpec name and the build-error case. The finding was stale because the reviewed range did not include commit 17138889f5.

The LoadSpec methods in the two mocks are not exercised. Recording name in their m.Called arguments has no test effect without a call site and a corresponding expectation. Keeping _ string is correct for the current tests.


✏️ Learnings added
Learnt from: yohamta0
URL: https://github.com/dagucloud/dagu/pull/2527

Timestamp: 2026-08-07T16:29:14.530Z
Learning: In this PR, `internal/persis/file/dag/store_test.go` covers `Storage.LoadSpec` with an explicit DAG name and with build-error handling. Verify the current PR head and commit range before reporting missing coverage for explicit `LoadSpec` names.

Learnt from: yohamta0
URL: https://github.com/dagucloud/dagu/pull/2527

Timestamp: 2026-08-07T16:29:14.530Z
Learning: In `internal/cmn/telemetry/collector_test.go` and `internal/runtime/agent/dbclient_test.go`, `mockDAGStore.LoadSpec` exists to satisfy the `exec.DAGStore` interface but is not called by the package tests. Do not require mock argument recording unless a test exercises `LoadSpec` and sets an expectation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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/persis/file/dag/store_test.go (1)

364-372: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the GetDetails build-error path.

The test covers AllowBuildErrors only through LoadSpec. Add a DAG file with a build error and assert that GetDetails(ctx, name, exec.DAGLoadOptions{AllowBuildErrors: true}) returns the partial DAG with BuildErrors. This verifies option translation in both storage methods.

As per coding guidelines, “Add or update tests appropriate to the changed code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/persis/file/dag/store_test.go` around lines 364 - 372, Extend the
GetDetails test around the existing detailed-dag assertions by creating a DAG
file with a build error, then call GetDetails with
exec.DAGLoadOptions{AllowBuildErrors: true}. Assert that it returns the partial
DAG and exposes the expected BuildErrors, covering option translation
independently of the existing LoadSpec test.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/core/restore.go`:
- Around line 27-45: Add regression tests covering RestoreUnpersistedFrom:
verify JSON-excluded configuration fields copy from src, while EnvEvaluated,
BuildErrors, and BuildWarnings remain unchanged; also test the separate Env
merge behavior through RebuildFromYAML. Use the existing DAG test conventions
and run the Go tests with race detection.

---

Nitpick comments:
In `@internal/persis/file/dag/store_test.go`:
- Around line 364-372: Extend the GetDetails test around the existing
detailed-dag assertions by creating a DAG file with a build error, then call
GetDetails with exec.DAGLoadOptions{AllowBuildErrors: true}. Assert that it
returns the partial DAG and exposes the expected BuildErrors, covering option
translation independently of the existing LoadSpec test.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 504c685a-9418-4b52-90c5-30d097d584ce

📥 Commits

Reviewing files that changed from the base of the PR and between a334c61 and 1713888.

📒 Files selected for processing (17)
  • internal/cmn/telemetry/collector_test.go
  • internal/core/exec/dag.go
  • internal/core/restore.go
  • internal/core/spec/rebuild.go
  • internal/intg/one_off_schedule_test.go
  • internal/intg/sched_test.go
  • internal/persis/file/dag/store.go
  • internal/persis/file/dag/store_test.go
  • internal/runtime/agent/dbclient.go
  • internal/runtime/agent/dbclient_test.go
  • internal/service/frontend/api/v1/dagruns_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_test.go
  • internal/service/frontend/api/v1/notifications.go
  • internal/service/frontend/api/v1/webhooks.go
  • internal/service/notification/service.go
  • internal/service/notification/service_test.go

Comment thread internal/core/restore.go
RestoreUnpersistedFrom decides both what a rebuild carries over and what
it must leave behind, and only the first half was exercised, indirectly,
by the rebuild tests. Assert both: the fields JSON omits arrive from the
rebuilt DAG, the fields it keeps survive untouched, and the build outcome
stays with the DAG being restored rather than being replaced by the
outcome of rebuilding it.

Also exercise AllowBuildErrors through GetDetails. Both store methods
translate the option through the same helper but assemble their loader
options separately, so a mistake in one would not surface in the other's
test.
@yohamta0

yohamta0 commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

The previous test asserted the restored values field by field. It caught a
misclassified field but not a newly added one, which is the oversight that
will actually happen: a new JSON-excluded field is restored by default, so
it slips through silently even when it describes the build and must not be.
Most of what it did assert was already covered by the rebuild tests.

Guard the classification instead. Every JSON-excluded field must be named
as restored or as build outcome, so adding one fails until the decision is
made, and removing the exclusion from a listed field fails as well.

RestoreUnpersistedFrom treats every field through one uniform loop, so the
loop is proven by any rebuild test that round-trips a field; asserting the
same twelve fields individually added lines without adding coverage.
The classification guard proves every JSON-excluded field is assigned a
side, but not that either side is honoured: deleting the exclusion check
in RestoreUnpersistedFrom left it green while EnvEvaluated, BuildErrors
and BuildWarnings were silently overwritten by the rebuild's own outcome.

Cover the contract directly. One configuration field must cross, and the
three outcome fields must keep the values of the DAG being restored.
@yohamta0
yohamta0 merged commit 25e7869 into main Aug 8, 2026
11 checks passed
@yohamta0
yohamta0 deleted the refactor/dagstore-load-options branch August 8, 2026 01:52
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