Skip to content

feat: surface passive value reference notices - #2290

Merged
yohamta0 merged 23 commits into
mainfrom
value-resolution-passive-diagnostics
Jun 17, 2026
Merged

feat: surface passive value reference notices#2290
yohamta0 merged 23 commits into
mainfrom
value-resolution-passive-diagnostics

Conversation

@yohamta0

@yohamta0 yohamta0 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • preserve unresolved supported value references and surface passive value-reference notices only in explicit inspection surfaces
  • expose spec-load valueReferenceNotices in REST/UI and dagu validate without run logs, run status, persistence, or runtime transport
  • simplify notice plumbing by removing the store-level LoadSpecWithResult API, generic diagnostic payloads, and no-op resolver validation
  • stabilize brittle Windows CI tests by asserting observable proc-store and notification behavior instead of raw timestamp changes or internal read counters

Testing

  • make api
  • cd ui && pnpm gen:api
  • cd ui && pnpm typecheck
  • GOCACHE=/private/tmp/dagu-go-cache go test -count=1 ./internal/cmn/value ./internal/core ./internal/core/spec ./internal/cmd ./internal/service/frontend/api/v1 ./internal/persis/file/proc ./internal/service/chatbridge ./internal/persis/file/dag ./internal/agent ./internal/cmn/telemetry ./internal/runtime/agent ./internal/service/notification -run 'TestResolverReportsDedupedValueReferenceNotices|TestResolverUnresolvedStrictReferencesPreserve|TestResolverPreservesUnsupportedSyntaxAsOrdinaryContent|TestLoadYAMLWithResultReturnsValueReferenceNotices|TestLoadYAMLWithResultInspectsWorkflowFieldsForValueReferenceNotices|TestValidateCommand/ValueReferenceNotices|TestGetDAGSpecIncludesValueReferenceNotices|TestStoreWritesReleasedProcFileLayoutOnly|TestNotificationMonitorWithoutDestinationsAdvancesCursorWithoutReadingEvents|TestNotificationMonitorDeliversOnlyFutureEventsAfterDestinationIsAdded'
  • git diff --check

Summary by CodeRabbit

  • New Features
    • Added a passive value reference notices system: unresolved references are now surfaced as notices (not warnings) in CLI validation and the DAG spec inspection response.
    • Updated GET /dags/{fileName}/spec to include valueReferenceNotices.
    • Enhanced the spec editor to show a ValueReferenceNoticesButton when notices are present.
  • Documentation
    • Updated value-resolution specifications to describe passive notice behavior for unresolved references.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 90 files

Re-trigger cubic

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eea02563-94a5-463d-93f6-9be3e52b8082

📥 Commits

Reviewing files that changed from the base of the PR and between 2c64a87 and fc52331.

📒 Files selected for processing (74)
  • Makefile
  • api/v1/api.gen.go
  • api/v1/api.yaml
  • conformance/harness/runner.go
  • conformance/spec004_consts/value_resolution_consts_test.go
  • conformance/spec005_params/testdata/env_list_forms.yaml
  • conformance/spec005_params/testdata/handler_run_array.yaml
  • conformance/spec005_params/testdata/handler_stderr_artifact.yaml
  • conformance/spec005_params/testdata/handler_stdout_artifact.yaml
  • conformance/spec005_params/testdata/parallel_items_params.yaml
  • conformance/spec005_params/testdata/parallel_items_value.yaml
  • conformance/spec005_params/testdata/parallel_variable.yaml
  • conformance/spec005_params/testdata/params_default_literal.yaml
  • conformance/spec005_params/testdata/params_reference_in_consts_runtime.yaml
  • conformance/spec005_params/testdata/root_shell_string.yaml
  • conformance/spec005_params/testdata/root_working_dir.yaml
  • conformance/spec005_params/testdata/step_output_nested.yaml
  • conformance/spec005_params/testdata/step_repeat_policy_condition.yaml
  • conformance/spec005_params/testdata/step_run_array.yaml
  • conformance/spec005_params/testdata/step_stderr_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_outputs.yaml
  • conformance/spec005_params/testdata/step_with_nested.yaml
  • conformance/spec005_params/value_resolution_params_test.go
  • internal/cmd/context.go
  • internal/cmd/validate.go
  • internal/cmd/validate_test.go
  • internal/cmn/value/notices.go
  • internal/cmn/value/references.go
  • internal/cmn/value/references_test.go
  • internal/cmn/value/resolver.go
  • internal/cmn/value/resolver_test.go
  • internal/cmn/value/template.go
  • internal/cmn/value/template_test.go
  • internal/core/dag.go
  • internal/core/output_reference_validation.go
  • internal/core/output_reference_validation_test.go
  • internal/core/spec/builder.go
  • internal/core/spec/consts.go
  • internal/core/spec/consts_test.go
  • internal/core/spec/dparams.go
  • internal/core/spec/dparams_inline.go
  • internal/core/spec/loader.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/spec/value_reference_fields_test.go
  • internal/core/spec/value_reference_notices_test.go
  • internal/core/spec/variables.go
  • internal/core/validator.go
  • internal/core/value_fields.go
  • internal/core/value_notices.go
  • internal/core/value_reference_validation_test.go
  • internal/launcher/launcher.go
  • internal/persis/file/proc/store_test.go
  • internal/runtime/env.go
  • internal/runtime/node.go
  • internal/service/chatbridge/monitor_external_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_internal_test.go
  • specs/003-value-resolution.md
  • specs/004-value-resolution-consts.md
  • specs/005-value-resolution-params.md
  • specs/006-value-resolution-env.md
  • specs/007-value-resolution-steps.md
  • specs/009-step-reference.md
  • specs/010-field-evaluation.md
  • specs/011-dynamic-evaluation.md
  • specs/013-step-run.md
  • specs/README.md
  • ui/src/api/v1/schema.ts
  • ui/src/features/dags/components/DAGStatus.tsx
  • ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx
  • ui/src/features/dags/components/dag-editor/DAGSpec.tsx
  • ui/src/features/dags/components/value-reference-notices/ValueReferenceNoticesButton.tsx
  • ui/src/features/dags/components/value-reference-notices/index.ts
💤 Files with no reviewable changes (9)
  • internal/core/output_reference_validation.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/output_reference_validation_test.go
  • internal/core/dag.go
  • internal/core/spec/dparams_inline.go
  • internal/cmn/value/references.go
  • internal/core/validator.go
  • internal/cmd/context.go
  • internal/cmn/value/references_test.go

📝 Walkthrough

Walkthrough

Replaces value-resolution validation warnings with a structured passive-notice mechanism (ValueReferenceNotice). Removes Resolver.Validate, Resolver.Warnings, validateReferences, output-reference validation, and binding-reference validation. Adds LoadWithResult/LoadYAMLWithResult APIs that collect and return notices. Exposes notices in the GET /dags/{fileName}/spec API and dagu validate CLI. Adds a ValueReferenceNoticesButton UI component. Expands spec005 conformance coverage with 18 new test fixtures and marks it fully implemented.

Changes

Passive Notices for Value Resolution

Layer / File(s) Summary
ValueReferenceNotice types, Collector, and Sink
internal/cmn/value/notices.go
Introduces ValueReferenceNotice struct (message, fieldPath, token), ValueReferenceNoticeSink interface, ValueReferenceNoticeCollector with (FieldPath, Token) deduplication, and addUnresolvedReferenceNotice helper for formatting and reporting unresolved references.
Resolver refactoring: remove warnings, add notice sink, drop policy mode
internal/cmn/value/resolver.go, internal/cmn/value/references.go, internal/cmn/value/template.go, internal/cmn/value/resolver_test.go, internal/cmn/value/references_test.go, internal/cmn/value/template_test.go
Removes Resolver.Validate, Resolver.Warnings, validateReferences, warnUnresolvedBinding helpers, and staticScope. Adds WithValueReferenceNotices option to wire sink into Resolver. resolveString delegates strict resolution to resolveBindings with sink. Removes mode field from resolverPolicy; policyForField and workflowValuePolicy are rewritten. Test coverage updated to notice semantics.
Validation cleanup: output-reference, binding-reference, and parameter-eval validation removed
internal/core/output_reference_validation.go, internal/core/output_reference_validation_test.go, internal/core/validator.go, internal/core/dag.go, internal/core/spec/dparams.go, internal/core/spec/dparams_inline.go
Deletes output_reference_validation.go and its test suite. Removes validateBindingReferences and appendBuildWarning from validator.go. dag.go stops recording output-reference warnings in Validate(). dparams.go removes validateParamEvalReferences and validateNoDaguReferencesInParamLiteral from plan finalization. dparams_inline.go removes validateNoDaguReferencesInParamLiteral checks for description, pattern, enum, and default fields.
BuildContext sink, consts/variables wiring, ReferenceField notice paths, and ReportValueReferenceNotices
internal/core/spec/builder.go, internal/core/spec/consts.go, internal/core/spec/variables.go, internal/core/value_fields.go, internal/core/value_notices.go
BuildContext gains valueReferenceNotices sink field. buildConsts passes BuildContext into resolveConstValue; resolver context derives from ctx.ctx. evaluatePairs wires WithValueReferenceNotices with index-based field labeling. ReferenceField gains noticePath override with noticeFieldPath(), withNoticePath(), and commandEntryNoticePath() helper. ReportValueReferenceNotices iterates ReferenceFields with path-stamping sink wrapper.
Spec loader: LoadResult struct and LoadWithResult/LoadYAMLWithResult APIs
internal/core/spec/loader.go, internal/core/spec/value_reference_notices_test.go
Introduces LoadResult struct holding *core.DAG and []ValueReferenceNotice. Adds LoadWithResult and LoadYAMLWithResult public APIs that collect notices during DAG loading via ValueReferenceNoticeCollector attached to BuildContext and call ReportValueReferenceNotices after load. Tests assert notices are returned for unresolved const and params references.
Spec tests: consts, fields, and value-reference validation updated for notice semantics
internal/core/spec/consts_test.go, internal/core/spec/value_reference_fields_test.go, internal/core/value_reference_validation_test.go
Adds tests for preserved unavailable and unsupported-syntax const references. Removes wantWarning from table-driven tests; renames tests from "warns" to "preserves". Drops BuildWarnings assertions. Deletes output_reference_validation_test.go.
Launcher and runtime: io.Writer generalization and context threading
internal/launcher/launcher.go, internal/runtime/env.go, internal/runtime/node.go
CmdSpec.Stdout and Stderr change from *os.File to io.Writer. All strict/non-strict working-directory helpers in env.go thread context.Context for cancellation/timeout propagation. node.go computes conditional field paths using commandEntryFieldPath helper for single vs. indexed run paths.
API service: notices in GetDAGSpec response and validate CLI
api/v1/api.yaml, internal/service/frontend/api/v1/dags.go, internal/service/frontend/api/v1/dags_internal_test.go, internal/cmd/validate.go, internal/cmd/validate_test.go, internal/cmd/context.go, ui/src/api/v1/schema.ts
Adds ValueReferenceNotice schema to OpenAPI spec and TypeScript schema; extends GET /dags/{fileName}/spec (200 response) and getDAGSpec with valueReferenceNotices field. GetDAGSpec calls spec.LoadYAMLWithResult with workspace config; runValidate uses spec.LoadWithResult and logs notices. Tests assert notices surface at both endpoints.
Frontend: ValueReferenceNoticesButton component and DAGSpec integration
ui/src/features/dags/components/value-reference-notices/ValueReferenceNoticesButton.tsx, ui/src/features/dags/components/value-reference-notices/index.ts, ui/src/features/dags/components/dag-editor/DAGSpec.tsx, ui/src/features/dags/components/DAGStatus.tsx, ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx
Adds ValueReferenceNoticesButton with count badge and scrollable dialog. DAGSpec.tsx derives notices from SWR data, adds SSE cache sync effect, refactors editorHeaderActions to show button when notices present. Tab layout adjustments in DAGStatus.tsx and DAGDetailsContent.tsx switch to 2xl breakpoint.
Conformance tests, spec005 expansion, spec documentation, and Makefile
Makefile, conformance/harness/runner.go, conformance/spec004_consts/value_resolution_consts_test.go, conformance/spec005_params/testdata/*, conformance/spec005_params/value_resolution_params_test.go, specs/003-013.md, specs/README.md, internal/persis/file/proc/store_test.go, internal/service/chatbridge/monitor_external_test.go
Adds CONFORMANCE_GO_TEST_FLAGS with -parallel=4. Harness gains ExpectFileContains and ExpectGlobFileContent with safety constraints. spec004 reclassifies warningCases as noticeCases. spec005 adds 18 testdata YAML fixtures and substantially expands the test with glob, contains, and missingArgs assertion strategies. Specs 003–013 replace "warning" with "passive notice/diagnostic". specs/README marks spec005 as implemented. Unrelated proc/store and chatbridge monitor test synchronization fixes included.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GetDAGSpec
  participant LoadYAMLWithResult
  participant ValueReferenceNoticeCollector
  participant ReportValueReferenceNotices

  Client->>GetDAGSpec: GET /dags/{fileName}/spec
  GetDAGSpec->>LoadYAMLWithResult: data, opts (WithoutEval, workspace)
  LoadYAMLWithResult->>ValueReferenceNoticeCollector: create collector
  LoadYAMLWithResult->>LoadYAMLWithResult: attach collector to BuildContext
  LoadYAMLWithResult->>ReportValueReferenceNotices: resolve ReferenceFields, Report unresolved
  ReportValueReferenceNotices->>ValueReferenceNoticeCollector: Report(ValueReferenceNotice)
  LoadYAMLWithResult-->>GetDAGSpec: LoadResult{DAG, ValueReferenceNotices}
  GetDAGSpec-->>Client: 200 {spec, errors, valueReferenceNotices:[{message, fieldPath, token}]}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • dagucloud/dagu#2105: This PR deletes internal/core/output_reference_validation.go entirely, which was introduced by that PR adding step-output contract validation and the validateOutputReferences pass.
  • dagucloud/dagu#2255: Overlapping UI layout adjustments in DAGStatus.tsx and DAGDetailsContent.tsx that address the same tab/header overflow behavior.
  • dagucloud/dagu#2285: Lays the core value-resolution/template/reference/resolver machinery in internal/cmn/value/*; this PR builds on those components by refactoring strict unresolved behavior to emit ValueReferenceNotices and wiring them through loader/validate/API.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch value-resolution-passive-diagnostics

@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.

Caution

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

⚠️ Outside diff range comments (1)
api/v1/api.yaml (1)

12963-12992: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add diagnostics to the DAGRunDetails required list.

The property is declared but omitted from required, so generated clients will treat run diagnostics as optional while the API is meant to return a stable array contract. Represent unavailable diagnostics as [] and require the field here. Based on PR context, DAG-run details expose transient diagnostics through the API response.

🔧 Proposed OpenAPI contract fix
           required:
             - rootDAGRunName
             - rootDAGRunId
             - log
             - nodes
+            - diagnostics
🤖 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 `@api/v1/api.yaml` around lines 12963 - 12992, The diagnostics property is
declared in the DAGRunDetails schema but is missing from the required fields
list. Add diagnostics to the required array in the DAGRunDetails schema
alongside the existing required properties rootDAGRunName, rootDAGRunId, log,
and nodes to ensure the API contract reflects that diagnostics will always be
returned as a stable array (even if empty when unavailable).
🧹 Nitpick comments (2)
internal/diagnostic/stream.go (1)

52-61: 💤 Low value

Write errors are silently ignored (fire-and-forget diagnostic semantics).

The implementation ignores both JSON marshaling errors (line 55) and write errors (line 60). For the Diagnostic struct with primitive fields, marshaling should never fail. Write failures will silently drop diagnostics, which is acceptable for best-effort diagnostic reporting but means diagnostics can be lost if the writer encounters I/O errors.

This is a reasonable design choice for non-critical diagnostic output.

🤖 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/diagnostic/stream.go` around lines 52 - 61, The Report method in the
streamSink struct intentionally ignores both JSON marshaling errors and write
errors to implement fire-and-forget diagnostic semantics. Add clear comments
above the error handling sections (where err is checked after json.Marshal and
where fmt.Fprintf is called) to explicitly document that this silent dropping of
errors is intentional and acceptable for best-effort diagnostic reporting.
Document that marshaling errors should be rare with the Diagnostic struct's
primitive fields, and that write failures are acceptable trade-offs for
non-critical diagnostic output.
ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx (1)

152-154: ⚡ Quick win

Note the significant breakpoint change from lg to 2xl.

Changing the responsive breakpoint from lg (1024px) to 2xl (1536px) means the desktop tab layout will only appear on larger screens. Users on tablets and smaller laptops (1024px–1535px) will now see the compact icon-only tab layout instead of the full labeled tabs. Ensure this aligns with the intended design and has been tested across target screen sizes.

Also applies to: 296-296

🤖 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 `@ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx` around
lines 152 - 154, Verify the responsive breakpoint change in the
DAGDetailsContent component where the desktop tab layout was changed from `lg`
(1024px) to `2xl` (1536px). If this change was unintentional, revert the
`2xl:block` and `2xl:flex-row` classes back to `lg:block` and `lg:flex-row`
respectively to restore the original responsive behavior for tablets and smaller
laptops. If the change was intentional, ensure it aligns with the design
specification and test the layout across all target screen sizes including
1024px–1535px range. Note that this change also applies to the related classes
at line 296, so update both locations consistently.
🤖 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.

Outside diff comments:
In `@api/v1/api.yaml`:
- Around line 12963-12992: The diagnostics property is declared in the
DAGRunDetails schema but is missing from the required fields list. Add
diagnostics to the required array in the DAGRunDetails schema alongside the
existing required properties rootDAGRunName, rootDAGRunId, log, and nodes to
ensure the API contract reflects that diagnostics will always be returned as a
stable array (even if empty when unavailable).

---

Nitpick comments:
In `@internal/diagnostic/stream.go`:
- Around line 52-61: The Report method in the streamSink struct intentionally
ignores both JSON marshaling errors and write errors to implement
fire-and-forget diagnostic semantics. Add clear comments above the error
handling sections (where err is checked after json.Marshal and where fmt.Fprintf
is called) to explicitly document that this silent dropping of errors is
intentional and acceptable for best-effort diagnostic reporting. Document that
marshaling errors should be rare with the Diagnostic struct's primitive fields,
and that write failures are acceptable trade-offs for non-critical diagnostic
output.

In `@ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx`:
- Around line 152-154: Verify the responsive breakpoint change in the
DAGDetailsContent component where the desktop tab layout was changed from `lg`
(1024px) to `2xl` (1536px). If this change was unintentional, revert the
`2xl:block` and `2xl:flex-row` classes back to `lg:block` and `lg:flex-row`
respectively to restore the original responsive behavior for tablets and smaller
laptops. If the change was intentional, ensure it aligns with the design
specification and test the layout across all target screen sizes including
1024px–1535px range. Note that this change also applies to the related classes
at line 296, so update both locations consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aa072b75-af6f-40aa-a080-175daed24be3

📥 Commits

Reviewing files that changed from the base of the PR and between 2c64a87 and 5d2dc0e.

📒 Files selected for processing (90)
  • Makefile
  • api/v1/api.gen.go
  • api/v1/api.yaml
  • conformance/harness/runner.go
  • conformance/spec004_consts/value_resolution_consts_test.go
  • conformance/spec005_params/testdata/env_list_forms.yaml
  • conformance/spec005_params/testdata/handler_run_array.yaml
  • conformance/spec005_params/testdata/handler_stderr_artifact.yaml
  • conformance/spec005_params/testdata/handler_stdout_artifact.yaml
  • conformance/spec005_params/testdata/parallel_items_params.yaml
  • conformance/spec005_params/testdata/parallel_items_value.yaml
  • conformance/spec005_params/testdata/parallel_variable.yaml
  • conformance/spec005_params/testdata/params_default_literal.yaml
  • conformance/spec005_params/testdata/params_reference_in_consts_runtime.yaml
  • conformance/spec005_params/testdata/root_shell_string.yaml
  • conformance/spec005_params/testdata/root_working_dir.yaml
  • conformance/spec005_params/testdata/step_output_nested.yaml
  • conformance/spec005_params/testdata/step_repeat_policy_condition.yaml
  • conformance/spec005_params/testdata/step_run_array.yaml
  • conformance/spec005_params/testdata/step_stderr_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_outputs.yaml
  • conformance/spec005_params/testdata/step_with_nested.yaml
  • conformance/spec005_params/value_resolution_params_test.go
  • internal/agent/dag_manage_test.go
  • internal/cmd/context.go
  • internal/cmd/dry.go
  • internal/cmd/migrator_test.go
  • internal/cmd/restart.go
  • internal/cmd/retry.go
  • internal/cmd/start.go
  • internal/cmn/telemetry/collector_test.go
  • internal/cmn/value/diagnostics.go
  • internal/cmn/value/references.go
  • internal/cmn/value/resolver.go
  • internal/cmn/value/resolver_test.go
  • internal/cmn/value/template.go
  • internal/core/dag.go
  • internal/core/exec/dag.go
  • internal/core/output_reference_validation.go
  • internal/core/output_reference_validation_test.go
  • internal/core/spec/builder.go
  • internal/core/spec/consts.go
  • internal/core/spec/consts_test.go
  • internal/core/spec/diagnostics_test.go
  • internal/core/spec/loader.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/spec/value_reference_fields_test.go
  • internal/core/spec/variables.go
  • internal/core/validator.go
  • internal/core/value_reference_validation_test.go
  • internal/diagnostic/collector.go
  • internal/diagnostic/collector_test.go
  • internal/diagnostic/context.go
  • internal/diagnostic/context_test.go
  • internal/diagnostic/diagnostic.go
  • internal/diagnostic/run_store.go
  • internal/diagnostic/stream.go
  • internal/launcher/diagnostic_stream.go
  • internal/launcher/launcher.go
  • internal/launcher/launcher_test.go
  • internal/persis/file/dag/store.go
  • internal/runtime/agent/agent.go
  • internal/runtime/agent/agent_test.go
  • internal/runtime/agent/dbclient_test.go
  • internal/runtime/env.go
  • internal/runtime/node.go
  • internal/service/frontend/api/v1/api.go
  • internal/service/frontend/api/v1/dagruns.go
  • internal/service/frontend/api/v1/dagruns_edit_retry.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_internal_test.go
  • internal/service/frontend/api/v1/diagnostics.go
  • internal/service/notification/service_test.go
  • specs/003-value-resolution.md
  • specs/004-value-resolution-consts.md
  • specs/005-value-resolution-params.md
  • specs/006-value-resolution-env.md
  • specs/007-value-resolution-steps.md
  • specs/009-step-reference.md
  • specs/010-field-evaluation.md
  • specs/011-dynamic-evaluation.md
  • specs/013-step-run.md
  • specs/README.md
  • ui/src/api/v1/schema.ts
  • ui/src/features/dags/components/DAGStatus.tsx
  • ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx
  • ui/src/features/dags/components/dag-editor/DAGSpec.tsx
  • ui/src/features/dags/components/diagnostics/DiagnosticsButton.tsx
  • ui/src/features/dags/components/diagnostics/index.ts
💤 Files with no reviewable changes (6)
  • internal/core/dag.go
  • internal/core/output_reference_validation.go
  • internal/core/validator.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/output_reference_validation_test.go
  • internal/cmn/value/references.go

@yohamta0

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@yohamta0 yohamta0 changed the title feat: surface passive value diagnostics feat: surface passive value reference notices Jun 17, 2026
@yohamta0

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

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

Caution

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

⚠️ Outside diff range comments (2)
internal/cmn/value/template.go (1)

34-42: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve the original unresolved token text instead of rebuilding it from the path.

Line 37 rebuilds the token as "${" + path + "}", which can normalize whitespace and change the original literal text. Use the matched token from walkBindings so unresolved references are truly left unchanged.

Proposed fix
-resolved, err := walkBindings(input, func(_ string, path string) (string, error) {
+resolved, err := walkBindings(input, func(token string, path string) (string, error) {
 	value, err := bindingValue(ctx, path, scope, true)
 	if err != nil {
-		token := "${" + path + "}"
 		addUnresolvedReferenceNotice(notices, field, token, err)
 		placeholder := uniqueToken(seed, "__DAGU_UNRESOLVED_REF__")
 		seed += placeholder
 		protected[placeholder] = token
 		return placeholder, nil
 	}
🤖 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/value/template.go` around lines 34 - 42, The code rebuilds the
unresolved token as "${" + path + "}" on line 37, which can normalize whitespace
and alter the original literal text. Modify the walkBindings callback to receive
the original matched token text (not just the path) as a parameter, then use
that original token value instead of reconstructing it when calling
addUnresolvedReferenceNotice and when storing in the protected map. This ensures
unresolved references preserve their exact original formatting without any
normalization.
ui/src/features/dags/components/dag-editor/DAGSpec.tsx (1)

153-161: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep existing notices in cache during SSE sync instead of clearing them.

Line 159 hard-resets valueReferenceNotices to [], which makes the notices UI disappear until the follow-up revalidation completes.

💡 Proposed fix
   useSSECacheSync(dagSSE, mutateSpec, (next) =>
     next.spec === undefined
       ? undefined
       : {
           dag: next.dag,
           errors: next.errors ?? [],
-          valueReferenceNotices: [],
+          valueReferenceNotices: data?.valueReferenceNotices ?? [],
           spec: next.spec,
         }
   );

As per coding guidelines, “NEVER use full-page loading overlays or LoadingIndicator components that hide content; instead show stale data while fetching updates.”

🤖 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 `@ui/src/features/dags/components/dag-editor/DAGSpec.tsx` around lines 153 -
161, The valueReferenceNotices property in the useSSECacheSync callback is being
hard-reset to an empty array, which clears the UI notices prematurely during SSE
updates. Instead of setting valueReferenceNotices to an empty array in the cache
update object, preserve the existing notices from the previous cache state so
users continue to see stale data while the SSE update completes. You can access
the current cache value to retrieve the previous valueReferenceNotices and
maintain them during the sync operation.

Source: Coding guidelines

🧹 Nitpick comments (2)
internal/service/frontend/api/v1/dags_internal_test.go (1)

831-837: ⚡ Quick win

Prefer asserting stable notice contract fields over human-readable message text.

require.Contains(notice.Message, "was left unchanged") is fragile to wording changes. Assert Kind/Code (and keep FieldPath/Token) so this test tracks API contract, not copy text.

Suggested test hardening
  notice := specResp.ValueReferenceNotices[0]
+ require.NotNil(t, notice.Kind)
+ require.Equal(t, "value_resolution", *notice.Kind)
+ require.NotNil(t, notice.Code)
+ require.Equal(t, "value_reference_unresolved", *notice.Code)
  require.NotNil(t, notice.FieldPath)
  require.Equal(t, "consts.image", *notice.FieldPath)
  require.NotNil(t, notice.Token)
  require.Equal(t, "${consts.missing}", *notice.Token)
- require.Contains(t, notice.Message, "was left unchanged")
+ require.NotEmpty(t, notice.Message)
🤖 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/service/frontend/api/v1/dags_internal_test.go` around lines 831 -
837, The test at the end of the test block is asserting on the human-readable
message text of the notice object, which is fragile to wording changes. Replace
the require.Contains assertion on notice.Message with assertions on the stable
API contract fields like notice.Kind and notice.Code. Keep the existing
assertions on notice.FieldPath and notice.Token, and verify that the Kind and
Code fields match the expected contract for this type of notice rather than
checking for specific message text.
internal/core/spec/value_reference_notices_test.go (1)

31-34: ⚡ Quick win

Assert kind and code explicitly in notice tests.

Current checks rely on message text, which is more brittle than the structured contract fields this feature introduces.

Suggested assertion additions
  got := result.ValueReferenceNotices[0]
+ assert.Equal(t, "value_resolution", got.Kind)
+ assert.Equal(t, "value_reference_unresolved", got.Code)
  assert.Equal(t, "consts.image", got.FieldPath)
  assert.Equal(t, "${consts.missing}", got.Token)
  assert.Contains(t, got.Message, "was left unchanged")

Also applies to: 53-55

🤖 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/spec/value_reference_notices_test.go` around lines 31 - 34, The
test assertions are relying on the Message field to verify behavior, which is
brittle. Add explicit assertions for the kind and code fields of the got notice
object alongside the existing FieldPath, Token, and Message checks. This applies
to both test cases mentioned in the review (around lines 31-34 and 53-55).
Assert the appropriate kind and code values that correspond to the expected
notice type, making the test more robust by checking the structured contract
fields instead of just the message text.
🤖 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/spec/variables.go`:
- Around line 117-123: The field path passed to DAGEnvField in the resolver
initialization is using "env." + p.key format, which differs from the env[i]
format used during post-load reference inspection, causing duplicate diagnostics
for unresolved tokens. Normalize the field path passed to DAGEnvField to use a
consistent format that matches what post-load reference inspection will use,
likely changing from "env." + p.key to a normalized path such as just "env" or
an array-index format to ensure both evaluation contexts report using the same
FieldPath for proper deduplication.

In `@internal/service/chatbridge/monitor_external_test.go`:
- Around line 154-156: The test is missing the assertion to verify that no reads
occurred, which is part of the test's contract as indicated by the test name.
After the require.Eventually block that waits for store.lastHead() to advance to
at least 1, add an assertion to check that readCalls equals 0 to ensure the
no-destination fast-path behavior is properly guarded and the test validates
both cursor advancement and the absence of read operations.

In `@specs/003-value-resolution.md`:
- Around line 134-136: Update the statement about inspection surfaces on line
134 to include the REST API inspection surface alongside the existing mentions
of dagu validate and the Web UI spec editor. Add explicit documentation that
notices also surface via the spec inspection API response so that all three
interfaces (CLI, Web UI, and REST API) are properly documented in the
specification.

In `@ui/src/features/dags/components/DAGStatus.tsx`:
- Around line 447-507: Add aria-label attributes to each Tab component to
provide accessible names for screen readers, since the text labels are hidden on
small screens with the hidden sm:inline class. For each Tab with activeTab
values of 'status', 'approval', 'timeline', 'outputs', 'artifacts', 'chat', and
'spec', add an aria-label prop with the corresponding label text (e.g.,
aria-label="Status", aria-label="Approval", etc.) to ensure the tabs remain
accessible to assistive technologies even when the visual labels are not
displayed.

In
`@ui/src/features/dags/components/value-reference-notices/ValueReferenceNoticesButton.tsx`:
- Line 101: The `<p>` element rendering `notice.message` lacks text wrapping
utilities, causing long strings to overflow and break the dialog layout in
narrow widths. Add the Tailwind CSS classes `whitespace-normal break-words` to
the className attribute of the `<p>` element that displays notice.message to
ensure proper text wrapping and prevent layout overflow.

---

Outside diff comments:
In `@internal/cmn/value/template.go`:
- Around line 34-42: The code rebuilds the unresolved token as "${" + path + "}"
on line 37, which can normalize whitespace and alter the original literal text.
Modify the walkBindings callback to receive the original matched token text (not
just the path) as a parameter, then use that original token value instead of
reconstructing it when calling addUnresolvedReferenceNotice and when storing in
the protected map. This ensures unresolved references preserve their exact
original formatting without any normalization.

In `@ui/src/features/dags/components/dag-editor/DAGSpec.tsx`:
- Around line 153-161: The valueReferenceNotices property in the useSSECacheSync
callback is being hard-reset to an empty array, which clears the UI notices
prematurely during SSE updates. Instead of setting valueReferenceNotices to an
empty array in the cache update object, preserve the existing notices from the
previous cache state so users continue to see stale data while the SSE update
completes. You can access the current cache value to retrieve the previous
valueReferenceNotices and maintain them during the sync operation.

---

Nitpick comments:
In `@internal/core/spec/value_reference_notices_test.go`:
- Around line 31-34: The test assertions are relying on the Message field to
verify behavior, which is brittle. Add explicit assertions for the kind and code
fields of the got notice object alongside the existing FieldPath, Token, and
Message checks. This applies to both test cases mentioned in the review (around
lines 31-34 and 53-55). Assert the appropriate kind and code values that
correspond to the expected notice type, making the test more robust by checking
the structured contract fields instead of just the message text.

In `@internal/service/frontend/api/v1/dags_internal_test.go`:
- Around line 831-837: The test at the end of the test block is asserting on the
human-readable message text of the notice object, which is fragile to wording
changes. Replace the require.Contains assertion on notice.Message with
assertions on the stable API contract fields like notice.Kind and notice.Code.
Keep the existing assertions on notice.FieldPath and notice.Token, and verify
that the Kind and Code fields match the expected contract for this type of
notice rather than checking for specific message text.
🪄 Autofix (Beta)

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

Run ID: da5f2db3-d456-4e77-a6a3-95ab11aefebd

📥 Commits

Reviewing files that changed from the base of the PR and between 2c64a87 and 85485fc.

📒 Files selected for processing (74)
  • Makefile
  • api/v1/api.gen.go
  • api/v1/api.yaml
  • conformance/harness/runner.go
  • conformance/spec004_consts/value_resolution_consts_test.go
  • conformance/spec005_params/testdata/env_list_forms.yaml
  • conformance/spec005_params/testdata/handler_run_array.yaml
  • conformance/spec005_params/testdata/handler_stderr_artifact.yaml
  • conformance/spec005_params/testdata/handler_stdout_artifact.yaml
  • conformance/spec005_params/testdata/parallel_items_params.yaml
  • conformance/spec005_params/testdata/parallel_items_value.yaml
  • conformance/spec005_params/testdata/parallel_variable.yaml
  • conformance/spec005_params/testdata/params_default_literal.yaml
  • conformance/spec005_params/testdata/params_reference_in_consts_runtime.yaml
  • conformance/spec005_params/testdata/root_shell_string.yaml
  • conformance/spec005_params/testdata/root_working_dir.yaml
  • conformance/spec005_params/testdata/step_output_nested.yaml
  • conformance/spec005_params/testdata/step_repeat_policy_condition.yaml
  • conformance/spec005_params/testdata/step_run_array.yaml
  • conformance/spec005_params/testdata/step_stderr_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_artifact.yaml
  • conformance/spec005_params/testdata/step_stdout_outputs.yaml
  • conformance/spec005_params/testdata/step_with_nested.yaml
  • conformance/spec005_params/value_resolution_params_test.go
  • internal/cmd/context.go
  • internal/cmd/validate.go
  • internal/cmd/validate_test.go
  • internal/cmn/value/notices.go
  • internal/cmn/value/references.go
  • internal/cmn/value/references_test.go
  • internal/cmn/value/resolver.go
  • internal/cmn/value/resolver_test.go
  • internal/cmn/value/template.go
  • internal/cmn/value/template_test.go
  • internal/core/dag.go
  • internal/core/output_reference_validation.go
  • internal/core/output_reference_validation_test.go
  • internal/core/spec/builder.go
  • internal/core/spec/consts.go
  • internal/core/spec/consts_test.go
  • internal/core/spec/dparams.go
  • internal/core/spec/dparams_inline.go
  • internal/core/spec/loader.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/spec/value_reference_fields_test.go
  • internal/core/spec/value_reference_notices_test.go
  • internal/core/spec/variables.go
  • internal/core/validator.go
  • internal/core/value_fields.go
  • internal/core/value_notices.go
  • internal/core/value_reference_validation_test.go
  • internal/launcher/launcher.go
  • internal/persis/file/proc/store_test.go
  • internal/runtime/env.go
  • internal/runtime/node.go
  • internal/service/chatbridge/monitor_external_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/dags_internal_test.go
  • specs/003-value-resolution.md
  • specs/004-value-resolution-consts.md
  • specs/005-value-resolution-params.md
  • specs/006-value-resolution-env.md
  • specs/007-value-resolution-steps.md
  • specs/009-step-reference.md
  • specs/010-field-evaluation.md
  • specs/011-dynamic-evaluation.md
  • specs/013-step-run.md
  • specs/README.md
  • ui/src/api/v1/schema.ts
  • ui/src/features/dags/components/DAGStatus.tsx
  • ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx
  • ui/src/features/dags/components/dag-editor/DAGSpec.tsx
  • ui/src/features/dags/components/value-reference-notices/ValueReferenceNoticesButton.tsx
  • ui/src/features/dags/components/value-reference-notices/index.ts
💤 Files with no reviewable changes (9)
  • internal/core/output_reference_validation.go
  • internal/core/spec/output_reference_validation_test.go
  • internal/core/dag.go
  • internal/core/output_reference_validation_test.go
  • internal/cmd/context.go
  • internal/cmn/value/references.go
  • internal/core/validator.go
  • internal/core/spec/dparams_inline.go
  • internal/cmn/value/references_test.go

Comment thread internal/core/spec/variables.go
Comment on lines 154 to 156
require.Eventually(t, func() bool {
_, readCalls := store.stats()
return store.lastHead() >= 1 && readCalls == 0
return store.lastHead() >= 1
}, time.Second, 10*time.Millisecond)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Restore the “no reads” assertion to match the test’s contract.

At Line 155, the test now only checks cursor advancement, but the test name says without reading events. Please also assert readCalls == 0 (after waiting for head advancement) so this test still guards the no-destination fast-path behavior.

Suggested patch
 require.Eventually(t, func() bool {
 		return store.lastHead() >= 1
 }, time.Second, 10*time.Millisecond)
+
+_, readCalls := store.stats()
+require.Equal(t, 0, readCalls)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
require.Eventually(t, func() bool {
_, readCalls := store.stats()
return store.lastHead() >= 1 && readCalls == 0
return store.lastHead() >= 1
}, time.Second, 10*time.Millisecond)
require.Eventually(t, func() bool {
return store.lastHead() >= 1
}, time.Second, 10*time.Millisecond)
_, readCalls := store.stats()
require.Equal(t, 0, readCalls)
🤖 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/service/chatbridge/monitor_external_test.go` around lines 154 - 156,
The test is missing the assertion to verify that no reads occurred, which is
part of the test's contract as indicated by the test name. After the
require.Eventually block that waits for store.lastHead() to advance to at least
1, add an assertion to check that readCalls equals 0 to ensure the
no-destination fast-path behavior is properly guarded and the test validates
both cursor advancement and the absence of read operations.

Comment thread specs/003-value-resolution.md Outdated
Comment thread ui/src/features/dags/components/DAGStatus.tsx
@yohamta0

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@yohamta0
yohamta0 merged commit 6a61c7f into main Jun 17, 2026
14 checks passed
@yohamta0
yohamta0 deleted the value-resolution-passive-diagnostics branch June 17, 2026 14:05
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