Skip to content

feat: complete runtime profile management#2246

Merged
yohamta0 merged 25 commits into
mainfrom
feat/runtime-profiles-complete-pr
Jun 2, 2026
Merged

feat: complete runtime profile management#2246
yohamta0 merged 25 commits into
mainfrom
feat/runtime-profiles-complete-pr

Conversation

@yohamta0

@yohamta0 yohamta0 commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • complete runtime profile management across API, persistence, scheduler/worker dispatch, run execution, and UI
  • add per-DAG default runtime profile settings outside DAG YAML, using profile as the API/UI request field
  • preserve selected run profiles for retries/reschedules and expose profile usage in DAG run list/details
  • enforce protected profile permissions when profiles are selected manually or configured as DAG defaults
  • keep persisted secret-derived outputs masked in user-visible run status

Notes

  • Profiles remain a control-plane concern; DAG YAML and runtime data-plane contracts do not grow a profile field.
  • Start/enqueue requests treat omitted profile as "use DAG default," empty profile as "run without a profile," and non-empty profile as an explicit override.
  • Retry and reschedule paths use the original run profile and cannot change it.

Testing

  • make api
  • pnpm gen:api
  • make fmt
  • make lint
  • make api-validate
  • pnpm typecheck
  • pnpm build
  • pnpm test -- src/features/dags/components/dag-execution/__tests__/StartDAGModal.test.tsx
  • go test ./internal/dagsettings ./internal/persis/store ./internal/service/frontend/api/v1 ./internal/service/scheduler ./internal/cmd/process -count=1
  • make test

yohamta0 added 9 commits June 1, 2026 18:31
Add managed runtime profile storage, resolution, and CLI commands for profile variables and secrets.

Propagate selected profiles through local runs, queued runs, retries, and distributed task metadata, while masking managed secret values in status output.
Retry now always inherits the source run profile from stored status and no longer accepts profile overrides through CLI, API, queue, scheduler, or subprocess retry paths.
Expose selected runtime profiles consistently in DAG-run summary and detail responses, omit empty profile values, and show profiles in DAG-run list views.
…-complete

# Conflicts:
#	internal/runtime/agent/agent.go
#	internal/runtime/executor/dag_runner.go
@coderabbitai

coderabbitai Bot commented Jun 1, 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

Run ID: d196ac67-2609-43ab-8cd9-336b1be021ae

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

This PR adds runtime profiles as a managed feature, exposes profile CRUD APIs and UI, allows selected profiles on DAG start and enqueue flows, propagates profile metadata through execution and retries, masks resolved secret values in status output, and displays the chosen profile on DAG runs.

Changes

Runtime profiles feature

Layer / File(s) Summary
Profile domain and persistence
internal/profile/*, internal/persis/store/profile.go, internal/persis/file/agent_stores.go, internal/agent/runtime_stores.go, internal/persis/store/profile_test.go, internal/profile/*test.go
Adds the runtime profile model, validation, secret reference format, resolver, store interface, file-backed and collection-backed persistence, and tests for profile creation, updates, listing, and resolution.
Execution propagation and status masking
internal/runtime/agent/*, internal/core/exec/*, internal/dagrun/intake/*, internal/runtime/executor/*, internal/proto/convert/*, proto/coordinator/v1/coordinator.proto, internal/service/worker/remote_handler.go, internal/service/scheduler/dag_executor.go, internal/launcher/*
Threads profileName and resolved profile metadata through queued, local, distributed, retry, scheduler, worker, and subflow execution paths, and masks resolved secret values in status output and related tests.
CLI commands and local execution wiring
internal/cmd/profile.go, internal/cmd/profile_flag.go, internal/cmd/start.go, internal/cmd/enqueue.go, internal/cmd/dry.go, internal/cmd/retry.go, internal/cmd/restart.go, internal/cmd/local_execution.go, internal/cmd/flags.go, cmd/main.go, cmd/main_test.go, internal/cmd/*test.go
Adds the profile command with CRUD and entry management subcommands, registers it on the root CLI, adds --profile for selected local flows, rejects that flag in remote contexts, and passes selected or inherited profiles into local execution paths.
API contracts and backend handlers
api/v1/api.yaml, internal/service/frontend/api/v1/*, internal/service/frontend/server.go
Adds /profiles REST endpoints and schemas, accepts optional profileName on run creation/enqueue endpoints, validates runnable profiles in backend handlers, exposes profile names in DAG-run responses, and wires the profile store into the frontend API.
UI pages, run selection, and run display
ui/src/pages/profiles/*, ui/src/features/dags/components/..., ui/src/features/dag-runs/components/..., ui/src/menu.tsx, ui/src/App.tsx, ui/src/contexts/AuthContext.tsx, ui/src/api/v1/schema.ts, rfcs/020_runtime_profiles.html
Adds the Profiles page and navigation, generated client types for profile APIs, profile selection in DAG start and enqueue dialogs, profile badges in DAG-run views, permission gating for protected profiles, related tests, and the runtime profiles RFC.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI as Web UI / CLI
  participant API as Frontend API
  participant Worker as Worker or Local Runner
  participant Agent as Runtime Agent
  participant Stores as ProfileStore + SecretStore

  User->>UI: create/select profile
  UI->>API: POST /profiles or start/enqueue with profileName
  API->>Stores: validate/load profile
  API->>Worker: launch run with profileName
  Worker->>Agent: build agent options
  Agent->>Stores: resolve profile variables and secrets
  Agent-->>Worker: status with profile metadata
  Worker-->>API: DAG run status
  API-->>UI: DAGRunSummary/ProfileName
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • dagucloud/dagu#2153: Both changes use the same secret-store resolution and masking plumbing that runtime profiles now depend on for secret entries.
  • dagucloud/dagu#2194: The new profile persistence layer is built on the persis collection abstractions introduced there.
  • dagucloud/dagu#1966: Both PRs modify DAG-run reschedule and enqueue paths in internal/service/frontend/api/v1/dagruns.go.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/runtime-profiles-complete-pr

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

1 issue found across 88 files

Partial review: This PR has more than 50 files, so cubic reviewed the highest-priority files first. During the trial, paid plans get a higher file limit.
You can try an ultrareview to bypass the file limit, comment @cubic-dev-ai ultrareview. Learn more.

Fix all with cubic | Re-trigger cubic

Comment thread internal/service/frontend/api/v1/dagruns_edit_retry.go Outdated

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

Caution

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

⚠️ Outside diff range comments (2)
ui/src/features/dags/components/dag-details/DAGDetailsSidePanel.tsx (1)

229-244: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing profileName parameter forwarding in handleEnqueue.

The handleEnqueue callback is typed as EnqueueHandler which requires 4 parameters (params, dagRunId, immediate, profileName), but the implementation only accepts and forwards 3 parameters.

Profile selection will be lost when DAG start/enqueue flows pass through this wrapper.

🔧 Fix: Accept and forward profileName
 const handleEnqueue = React.useCallback<EnqueueHandler>(
-  async (params, dagRunId, immediate) => {
+  async (params, dagRunId, immediate, profileName) => {
     if (!onEnqueue) {
       return;
     }

-    const result = await onEnqueue(params, dagRunId, immediate);
+    const result = await onEnqueue(params, dagRunId, immediate, profileName);
     setActiveTab('status');
     if (typeof result === 'string' && result) {
       setTrackedDagRunId(result);
     }
     await mutate();
     return result;
   },
   [mutate, onEnqueue]
 );
🤖 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/DAGDetailsSidePanel.tsx` around
lines 229 - 244, The handleEnqueue callback currently declared as EnqueueHandler
ignores the fourth parameter profileName, causing profile selection to be
dropped; update the handleEnqueue signature to accept (params, dagRunId,
immediate, profileName) and forward profileName when calling onEnqueue (i.e.,
await onEnqueue(params, dagRunId, immediate, profileName)), leaving the rest of
the logic (setActiveTab, setTrackedDagRunId, await mutate, return result)
unchanged so trackedDagRunId and mutate continue to work as before.
ui/src/features/dags/contexts/DAGContext.ts (1)

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

Add GPL v3 license headers to these files

These files are missing SPDX-License-Identifier: GPL-3.0-or-later:

  • ui/src/features/dags/contexts/DAGContext.ts
  • ui/src/features/dags/components/dag-execution/StartDAGModal.tsx
  • ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx
  • ui/src/features/dag-runs/components/dag-run-list/DAGRunCard.tsx

Run make addlicense to apply the headers.

🤖 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/contexts/DAGContext.ts` at line 1, Add the missing GPL
v3 SPDX header to the top of each affected module (DAGContext, StartDAGModal,
DAGStatusOverview, DAGRunCard) by running the repository license tooling: run
make addlicense to automatically prepend "SPDX-License-Identifier:
GPL-3.0-or-later" to ui/src/features/dags/contexts/DAGContext.ts,
ui/src/features/dags/components/dag-execution/StartDAGModal.tsx,
ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx and
ui/src/features/dag-runs/components/dag-run-list/DAGRunCard.tsx, then commit the
updated files; verify the header appears above the existing imports (e.g., above
the import React statement in DAGContext.ts).
🧹 Nitpick comments (6)
ui/src/features/dags/components/dag-execution/StartDAGModal.tsx (1)

251-801: ⚡ Quick win

Missing test coverage for profile submission flow.

The profile selection logic is well-implemented, but there's no test verifying that when a profile is selected and the form is submitted, onSubmit is called with the profileName argument. Consider adding a test case that selects a profile and verifies the fourth parameter is passed correctly.

🤖 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-execution/StartDAGModal.tsx` around lines
251 - 801, Add a unit test for StartDAGModal that mounts the component with a
non-empty activeProfiles array and a mock onSubmit, then simulate selecting a
profile from the runtime profile <Select> (use the profile.name value, not
NO_PROFILE_VALUE), fill any minimal required inputs (e.g., leave default params
or set dagRunId), click the submit button, and assert the mock onSubmit was
called with the expected fourth argument equal to the selected profile name;
reference StartDAGModal, the onSubmit prop, the runtime profile
Select/SelectItem, and the submit button click to locate where to interact and
assert.
internal/profile/store.go (1)

8-14: ⚡ Quick win

Add godoc comments to document the Store interface.

The Store interface lacks documentation explaining its purpose, error semantics, and implementation requirements. Consider documenting:

  • What the interface represents
  • Error behavior for GetByName when a profile is not found (specific error type vs. nil)
  • Whether implementations must be thread-safe
  • Validation responsibilities for Create and Update
📝 Proposed documentation enhancement
+// Store provides persistence operations for runtime profiles.
+// Implementations must be safe for concurrent use.
+// GetByName returns an error if the profile is not found.
 type Store interface {
+	// Create persists a new profile. Returns an error if a profile
+	// with the same name already exists.
 	Create(ctx context.Context, profile *Profile) error
+	
+	// GetByName retrieves a profile by name. Returns an error if not found.
 	GetByName(ctx context.Context, name string) (*Profile, error)
+	
+	// List returns all profiles.
 	List(ctx context.Context) ([]*Profile, error)
+	
+	// Update modifies an existing profile. Returns an error if not found.
 	Update(ctx context.Context, profile *Profile) error
+	
+	// Delete removes a profile by name. Returns an error if not found.
 	Delete(ctx context.Context, name string) error
 }
🤖 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/profile/store.go` around lines 8 - 14, Add GoDoc for the Store
interface describing its purpose (persistent CRUD for Profile entities) and
document semantics for each method: state that Create(ctx, *Profile) and
Update(ctx, *Profile) expect callers to validate business rules (or specify
which validations the implementation must perform), that GetByName(ctx, string)
returns (nil, ErrNotFound) or another clearly named error when a profile does
not exist (declare the expected error symbol), and describe List and Delete
behaviors and error conditions. Also state whether implementations must be safe
for concurrent use (thread-safe) and note any other implementation requirements
(transactions, uniqueness guarantees) so implementers know expectations;
reference the Store type and its methods Create, GetByName, List, Update, Delete
and the Profile type in the docblock.
internal/profile/secret_ref.go (1)

11-13: 💤 Low value

Add a doc comment to the exported SecretRef.

The exported function lacks a doc comment; revive (enabled in your golangci-lint v2 config) will flag this. A brief comment also clarifies the reference format contract that callers/storage depend on.

📝 Proposed doc comment
+// SecretRef builds the deterministic secret store reference for a runtime
+// profile entry, in the form "runtime-profiles/<profileName>/key-<hexKey>".
 func SecretRef(profileName, key string) string {
 	return fmt.Sprintf("runtime-profiles/%s/key-%s", profileName, hex.EncodeToString([]byte(key)))
 }
As per coding guidelines: "Use golangci-lint v2 with checks including errcheck, govet, staticcheck, gosec, and revive for Go linting".
🤖 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/profile/secret_ref.go` around lines 11 - 13, Add a proper Go doc
comment above the exported SecretRef function describing its purpose and the
exact reference format it returns (e.g.,
"runtime-profiles/<profileName>/key-<hexEncodedKey>") so revive/lint won't flag
it; place the comment immediately above the SecretRef declaration and keep it
concise, starting with "SecretRef ..." to follow Go doc conventions and clarify
the contract for callers/storage that depend on the reference format.
internal/profile/profile.go (1)

234-239: ⚡ Quick win

Enforce UTC normalization for all timestamps.

The normalizeTime helper converts zero times to time.Now().UTC() but preserves the timezone of non-zero times. This could lead to inconsistent timezone handling if callers pass times in other zones. For consistent storage and comparison, all timestamps should be normalized to UTC.

⏰ Proposed fix to enforce UTC
 func normalizeTime(t time.Time) time.Time {
 	if t.IsZero() {
 		return time.Now().UTC()
 	}
-	return t
+	return t.UTC()
 }
🤖 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/profile/profile.go` around lines 234 - 239, The normalizeTime helper
currently returns time.Now().UTC() for zero times but leaves non-zero times in
their original timezone; update normalizeTime to always return a UTC time by
returning time.Now().UTC() for zero values and calling t.UTC() for non-zero
values so all timestamps are consistently normalized to UTC (refer to the
normalizeTime function).
internal/cmd/profile.go (1)

255-283: 💤 Low value

Consider more robust whitespace trimming for stdin secret values.

Line 265 uses strings.TrimRight(string(data), "\r\n") which handles trailing carriage returns and newlines but not other trailing whitespace like tabs or spaces. For secret values read from stdin, consider using strings.TrimSpace for more consistent behavior, or document why only \r\n are trimmed.

💡 Alternative approach
-		return strings.TrimRight(string(data), "\r\n"), nil
+		return strings.TrimSpace(string(data)), 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/cmd/profile.go` around lines 255 - 283, The stdin branch in
runtimeProfileSecretValue currently uses strings.TrimRight(string(data), "\r\n")
which only removes CR/LF; replace that with strings.TrimSpace(string(data)) to
strip all leading/trailing whitespace (tabs, spaces, CR/LF) for secret values
read when the value-stdin flag is true (refer to useStdin and the "value-stdin"
flag handling). Ensure you only change the trimming of the data variable
returned from io.ReadAll and do not alter the term.ReadPassword path so behavior
remains consistent for interactive input.
internal/core/exec/context.go (1)

46-50: ⚡ Quick win

Add godoc comment for RuntimeProfileEntry.

The exported type RuntimeProfileEntry should have a package-level comment explaining what it represents and when it's used.

📝 Suggested documentation
+// RuntimeProfileEntry represents non-secret metadata about a single profile key
+// injected into a run. It records the key name and kind (variable or secret)
+// without exposing resolved values.
 type RuntimeProfileEntry struct {
 	Key  string `json:"key"`
 	Kind string `json:"kind"`
 }
🤖 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/context.go` around lines 46 - 50, Add a proper godoc
comment for the exported type RuntimeProfileEntry: insert a comment immediately
above the type declaration that begins with "RuntimeProfileEntry" and concisely
explains that it represents non‑secret metadata about a profile key that is
injected into a run (when/why it is used), and briefly describe the meaning of
the Key and Kind fields so future readers understand its purpose and usage in
the runtime execution context.
🤖 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 `@api/v1/api.yaml`:
- Around line 15287-15292: The RuntimeProfileKey schema currently allows keys
beginning with the reserved DAGU_ prefix; update the RuntimeProfileKey entry to
enforce that keys must still start with a letter or underscore and contain only
alphanumeric/underscore characters but must not begin with the literal "DAGU_";
implement this by adding a negative lookahead check to the existing pattern for
RuntimeProfileKey and update the description to state that DAGU_* keys are
rejected by validation rather than merely reserved.
- Around line 8413-8809: The operations for runtime profiles
(listRuntimeProfiles, createRuntimeProfile, getRuntimeProfile,
updateRuntimeProfile, deleteRuntimeProfile, setRuntimeProfileVariable,
setRuntimeProfileSecret, deleteRuntimeProfileEntry) currently inherit a global
permissive security requirement that allows unauthenticated access; add an
operation-level security override for each of these operations to require
authentication (for example add a security object listing the required schemes
such as apiToken and/or basicAuth) so each operation explicitly requires the API
token/basic auth instead of the empty global requirement.

In `@internal/runtime/agent/status_masking.go`:
- Around line 92-98: The unchecked type assertions on step.ExecutorConfig.Config
and step.ExecutorConfig.Metadata can panic if maskAnyStringValues returns a non
map[string]any; update the code to perform safe type checks (use a type
assertion with the "ok" pattern or a type switch) on the result of
maskAnyStringValues(masker, ...) before assigning back to
step.ExecutorConfig.Config or Metadata; if the result is a map[string]any assign
it, otherwise leave the original value or convert/handle it safely so no panic
occurs (refer to symbols: step.ExecutorConfig.Config,
step.ExecutorConfig.Metadata, and maskAnyStringValues(masker, ...)).

In `@internal/service/frontend/api/v1/dagruns_edit_retry.go`:
- Around line 85-89: EditRetryDAGRun fails to populate plan.profileName during
the real launch path, so the coordinator and executor never receive the
inherited profile; call inheritedRunProfileName in the execution path inside
EditRetryDAGRun (same way as in preview) to set plan.profileName when
plan.sourceStatus.ProfileName is present, then ensure the coordinator path uses
plan.profileName so executor.WithProfileName(...) is invoked; update references
in EditRetryDAGRun and any coordinator launch code to use the resolved
plan.profileName instead of a blank string.

In `@internal/service/frontend/api/v1/profiles.go`:
- Around line 64-83: ListRuntimeProfiles (and similarly GetRuntimeProfile)
currently only checks requireManagerOrAbove, allowing managers to read protected
profiles; enforce the same protected-profile restriction used by
requireRuntimeProfileManage/requireRuntimeProfileUpdate by checking whether each
profile is protected and requiring admin rights before exposing its values or
secret IDs. Update ListRuntimeProfiles to filter or redact protected entries (or
call a helper that performs requireRuntimeProfileManage-like checks per profile)
when serializing via toRuntimeProfileResponse, and apply the same change to
GetRuntimeProfile (and the other read handlers noted) so protected profiles are
only readable by admins.
- Around line 413-455: The ensureRunnableRuntimeProfile function currently
enforces requireManagerOrAbove unconditionally which blocks ordinary
profile-backed executions; remove the call to a.requireManagerOrAbove(ctx) and
instead only enforce role checks for protected profiles by keeping the existing
a.requireAdmin(ctx) inside the item.Protected branch (leave validation,
profileStore checks, and the disabled/not-found handling intact). Ensure the
function returns item.Name for non-protected profiles without requiring manager
privileges.

In `@internal/service/worker/remote_handler.go`:
- Around line 190-193: In handleRetry in
internal/service/worker/remote_handler.go, don’t let task.ProfileName override
the stored retry profile: replace the current precedence logic that starts with
profileName := task.ProfileName and falls back to status.ProfileName with logic
that unconditionally uses status.ProfileName (e.g., profileName :=
status.ProfileName), or assert that task.ProfileName is empty and fail/log if it
isn't; update all uses of profileName in handleRetry accordingly so retry
runtime profiles remain immutable and always come from status.ProfileName
(reference symbols: handleRetry, task.ProfileName, status.ProfileName,
task.PreviousStatus).

In `@rfcs/020_runtime_profiles.html`:
- Around line 930-936: In the "Retry Defaults" section, remove the CLI example
that shows overriding the profile (the `dagu retry --profile staging
20260601T143120Z` snippet) and update the paragraph to state that retries
inherit the original run's profile from stored status and that profile selection
is immutable across all retry paths (CLI/API/queue/scheduler/subprocess); ensure
the example shows only the immutable retry invocation (e.g., `dagu retry
20260601T143120Z`) and add a short clarifying sentence that profile overrides
are no longer accepted.

---

Outside diff comments:
In `@ui/src/features/dags/components/dag-details/DAGDetailsSidePanel.tsx`:
- Around line 229-244: The handleEnqueue callback currently declared as
EnqueueHandler ignores the fourth parameter profileName, causing profile
selection to be dropped; update the handleEnqueue signature to accept (params,
dagRunId, immediate, profileName) and forward profileName when calling onEnqueue
(i.e., await onEnqueue(params, dagRunId, immediate, profileName)), leaving the
rest of the logic (setActiveTab, setTrackedDagRunId, await mutate, return
result) unchanged so trackedDagRunId and mutate continue to work as before.

In `@ui/src/features/dags/contexts/DAGContext.ts`:
- Line 1: Add the missing GPL v3 SPDX header to the top of each affected module
(DAGContext, StartDAGModal, DAGStatusOverview, DAGRunCard) by running the
repository license tooling: run make addlicense to automatically prepend
"SPDX-License-Identifier: GPL-3.0-or-later" to
ui/src/features/dags/contexts/DAGContext.ts,
ui/src/features/dags/components/dag-execution/StartDAGModal.tsx,
ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx and
ui/src/features/dag-runs/components/dag-run-list/DAGRunCard.tsx, then commit the
updated files; verify the header appears above the existing imports (e.g., above
the import React statement in DAGContext.ts).

---

Nitpick comments:
In `@internal/cmd/profile.go`:
- Around line 255-283: The stdin branch in runtimeProfileSecretValue currently
uses strings.TrimRight(string(data), "\r\n") which only removes CR/LF; replace
that with strings.TrimSpace(string(data)) to strip all leading/trailing
whitespace (tabs, spaces, CR/LF) for secret values read when the value-stdin
flag is true (refer to useStdin and the "value-stdin" flag handling). Ensure you
only change the trimming of the data variable returned from io.ReadAll and do
not alter the term.ReadPassword path so behavior remains consistent for
interactive input.

In `@internal/core/exec/context.go`:
- Around line 46-50: Add a proper godoc comment for the exported type
RuntimeProfileEntry: insert a comment immediately above the type declaration
that begins with "RuntimeProfileEntry" and concisely explains that it represents
non‑secret metadata about a profile key that is injected into a run (when/why it
is used), and briefly describe the meaning of the Key and Kind fields so future
readers understand its purpose and usage in the runtime execution context.

In `@internal/profile/profile.go`:
- Around line 234-239: The normalizeTime helper currently returns
time.Now().UTC() for zero times but leaves non-zero times in their original
timezone; update normalizeTime to always return a UTC time by returning
time.Now().UTC() for zero values and calling t.UTC() for non-zero values so all
timestamps are consistently normalized to UTC (refer to the normalizeTime
function).

In `@internal/profile/secret_ref.go`:
- Around line 11-13: Add a proper Go doc comment above the exported SecretRef
function describing its purpose and the exact reference format it returns (e.g.,
"runtime-profiles/<profileName>/key-<hexEncodedKey>") so revive/lint won't flag
it; place the comment immediately above the SecretRef declaration and keep it
concise, starting with "SecretRef ..." to follow Go doc conventions and clarify
the contract for callers/storage that depend on the reference format.

In `@internal/profile/store.go`:
- Around line 8-14: Add GoDoc for the Store interface describing its purpose
(persistent CRUD for Profile entities) and document semantics for each method:
state that Create(ctx, *Profile) and Update(ctx, *Profile) expect callers to
validate business rules (or specify which validations the implementation must
perform), that GetByName(ctx, string) returns (nil, ErrNotFound) or another
clearly named error when a profile does not exist (declare the expected error
symbol), and describe List and Delete behaviors and error conditions. Also state
whether implementations must be safe for concurrent use (thread-safe) and note
any other implementation requirements (transactions, uniqueness guarantees) so
implementers know expectations; reference the Store type and its methods Create,
GetByName, List, Update, Delete and the Profile type in the docblock.

In `@ui/src/features/dags/components/dag-execution/StartDAGModal.tsx`:
- Around line 251-801: Add a unit test for StartDAGModal that mounts the
component with a non-empty activeProfiles array and a mock onSubmit, then
simulate selecting a profile from the runtime profile <Select> (use the
profile.name value, not NO_PROFILE_VALUE), fill any minimal required inputs
(e.g., leave default params or set dagRunId), click the submit button, and
assert the mock onSubmit was called with the expected fourth argument equal to
the selected profile name; reference StartDAGModal, the onSubmit prop, the
runtime profile Select/SelectItem, and the submit button click to locate where
to interact and assert.
🪄 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: 0e513909-7098-4c54-afd0-9448429d3a1c

📥 Commits

Reviewing files that changed from the base of the PR and between a4344a2 and aae8700.

⛔ Files ignored due to path filters (2)
  • proto/coordinator/v1/coordinator.pb.go is excluded by !**/*.pb.go
  • proto/coordinator/v1/coordinator_protoopaque.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (86)
  • api/v1/api.gen.go
  • api/v1/api.yaml
  • cmd/main.go
  • cmd/main_test.go
  • internal/agent/runtime_stores.go
  • internal/cmd/dry.go
  • internal/cmd/enqueue.go
  • internal/cmd/enqueue_internal_test.go
  • internal/cmd/exec.go
  • internal/cmd/flags.go
  • internal/cmd/local_execution.go
  • internal/cmd/profile.go
  • internal/cmd/profile_flag.go
  • internal/cmd/remote_commands.go
  • internal/cmd/restart.go
  • internal/cmd/retry.go
  • internal/cmd/retry_internal_test.go
  • internal/cmd/retry_test.go
  • internal/cmd/start.go
  • internal/core/exec/context.go
  • internal/core/exec/dispatch.go
  • internal/core/exec/enqueue_retry_test.go
  • internal/core/exec/runstatus.go
  • internal/dagrun/intake/local.go
  • internal/dagrun/intake/queue.go
  • internal/dagrun/intake/queue_test.go
  • internal/engine/run.go
  • internal/intg/one_off_schedule_test.go
  • internal/launcher/launcher.go
  • internal/launcher/launcher_test.go
  • internal/persis/file/agent_stores.go
  • internal/persis/store/profile.go
  • internal/persis/store/profile_test.go
  • internal/profile/profile.go
  • internal/profile/profile_test.go
  • internal/profile/resolver.go
  • internal/profile/resolver_test.go
  • internal/profile/secret_ref.go
  • internal/profile/store.go
  • internal/proto/convert/dispatch.go
  • internal/proto/convert/dispatch_test.go
  • internal/runtime/agent/agent.go
  • internal/runtime/agent/agent_test.go
  • internal/runtime/agent/status_masking.go
  • internal/runtime/context.go
  • internal/runtime/executor/dag_runner.go
  • internal/runtime/executor/subworkflow.go
  • internal/runtime/executor/task.go
  • internal/runtime/transform/status.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/dagruns_test.go
  • internal/service/frontend/api/v1/dags.go
  • internal/service/frontend/api/v1/profiles.go
  • internal/service/frontend/api/v1/profiles_test.go
  • internal/service/frontend/api/v1/transformer.go
  • internal/service/frontend/api/v1/transformer_test.go
  • internal/service/frontend/api/v1/webhooks.go
  • internal/service/frontend/server.go
  • internal/service/scheduler/dag_executor.go
  • internal/service/worker/remote_handler.go
  • internal/service/worker/remote_handler_test.go
  • internal/subflow/runner.go
  • proto/coordinator/v1/coordinator.proto
  • rfcs/020_runtime_profiles.html
  • ui/src/App.tsx
  • ui/src/__tests__/menu.test.tsx
  • ui/src/api/v1/schema.ts
  • ui/src/contexts/AuthContext.tsx
  • ui/src/features/cockpit/components/DAGPreviewModal.tsx
  • ui/src/features/dag-runs/components/dag-run-details/DAGRunHeader.tsx
  • ui/src/features/dag-runs/components/dag-run-list/DAGRunCard.tsx
  • ui/src/features/dag-runs/components/dag-run-list/DAGRunTable.tsx
  • ui/src/features/dag-runs/components/dag-run-list/__tests__/DAGRunTable.test.tsx
  • ui/src/features/dags/components/common/DAGActions.tsx
  • ui/src/features/dags/components/common/__tests__/DAGActions.test.tsx
  • ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx
  • ui/src/features/dags/components/dag-details/DAGDetailsSidePanel.tsx
  • ui/src/features/dags/components/dag-details/DAGStatusOverview.tsx
  • ui/src/features/dags/components/dag-execution/StartDAGModal.tsx
  • ui/src/features/dags/components/dag-execution/__tests__/StartDAGModal.test.tsx
  • ui/src/features/dags/contexts/DAGContext.ts
  • ui/src/menu.tsx
  • ui/src/pages/profiles/__tests__/index.test.tsx
  • ui/src/pages/profiles/index.tsx

Comment thread api/v1/api.yaml
Comment thread api/v1/api.yaml
Comment thread internal/runtime/agent/status_masking.go
Comment thread internal/service/frontend/api/v1/dagruns_edit_retry.go Outdated
Comment thread internal/service/frontend/api/v1/profiles.go
Comment thread internal/service/frontend/api/v1/profiles.go Outdated
Comment thread internal/service/frontend/api/v1/profiles.go
Comment thread internal/service/worker/remote_handler.go Outdated
Comment thread rfcs/020_runtime_profiles.html Outdated
@yohamta0

yohamta0 commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

yohamta0 added 3 commits June 2, 2026 11:21
Add persisted DAG settings for default runtime profiles and resolve them for manual and scheduled starts.

Keep retry behavior tied to the original run profile, support explicit no-profile overrides, and expose the setting in the UI.
@yohamta0

yohamta0 commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

@codex full review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 13021c366c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

func (a *API) inheritedRunProfileName(ctx context.Context, inherited string) (string, error) {
return a.ensureRunnableRuntimeProfile(ctx, strings.TrimSpace(inherited))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow inherited protected profiles without re-authorizing

When retrying or rescheduling a run that inherited a protected DAG default profile, this helper reuses ensureRunnableRuntimeProfile, which enforces admin access for protected profiles. Operators can start the same DAG with the protected default because the authorization happened when the default was configured, but retryDAGRun and rescheduleDAGRun call inheritedRunProfileName, so those non-admin retries/reschedules now fail with 403 despite not choosing a new explicit profile.

Useful? React with 👍 / 👎.

// Enqueue directly from the API layer so accepted webhook payloads do not
// need to fit in a subprocess command line.
if err := a.enqueuePreparedDAGRun(ctx, dag, params, dagRunID, core.TriggerTypeWebhook); err != nil {
if err := a.enqueuePreparedDAGRun(ctx, dag, params, dagRunID, core.TriggerTypeWebhook, ""); err != nil {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply DAG default profiles to webhook runs

For webhook-triggered DAGs, this hard-codes an empty profileName into enqueuePreparedDAGRun, so the queued status is created without the DAG's saved default profile and the agent never injects that profile's variables or secrets. Manual start/enqueue paths call runProfileForDAG and scheduled runs resolve the default, so a DAG that relies on its default runtime profile behaves differently when triggered through its webhook.

Useful? React with 👍 / 👎.

@yohamta0 yohamta0 merged commit c213570 into main Jun 2, 2026
12 checks passed
@yohamta0 yohamta0 deleted the feat/runtime-profiles-complete-pr branch June 2, 2026 15:23
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