Skip to content

Add process and I/O execution foundation - #2464

Merged
Andriy Knysh (aknysh) merged 6 commits into
mainfrom
codex/dag-process-io-foundation
May 30, 2026
Merged

Andriy Knysh (aknysh) merged 6 commits into
mainfrom
codex/dag-process-io-foundation

Conversation

@shirkevich

@shirkevich Mikhail Shirkov (shirkevich) commented May 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This is PR 1 for the DAG concurrent execution rollout. It introduces the reusable process and stream-isolation foundation without enabling scheduler behavior or changing Terraform bulk routing.

Changes:

  • Add pkg/process with Runner, TaskSpec, Streams, Result, default os/exec runner, context-aware execution, cancellation reporting, and exit-code preservation.
  • Extend pkg/io with prefixed per-node stream composition for terminal, file, and capture sinks.
  • Refactor internal/exec.ExecuteShellCommand() into a backward-compatible wrapper over pkg/process while preserving CI stdout/stderr capture options.
  • Replace the runTerraformShow() global os.Stdout swap with injected stdout capture.

Scope

No scheduler, CLI routing consolidation, concurrency flags, or Terraform adapter behavior is enabled in this PR.

Stacking

This PR is the bottom of the DAG rollout stack and targets main.

Supersedes the earlier fork-headed draft #2459 now that the stack branches exist in cloudposse/atmos.

Validation

rtk env GOCACHE=/private/tmp/atmos-gocache GOMODCACHE=/private/tmp/atmos-gomodcache go test ./pkg/process ./pkg/io ./internal/exec ./cmd/terraform

Next PR

PR 2 branches from codex/dag-process-io-foundation and adds the generic pkg/scheduler core with ready-queue scheduling, bounded workers, deterministic aggregate results, and isolated unit tests only.

Summary by CodeRabbit

Release Notes

  • New Features

    • Configurable subprocess execution with optional contexts and injectable streams
    • Composable, scope-scoped output writers with per-line prefixing and masking
  • Bug Fixes

    • More accurate subprocess exit/error reporting and improved stream-redirection behavior
  • Tests

    • Expanded unit tests for subprocess execution, stream injection/capture, and output utilities
  • Documentation

    • Updated concurrent execution docs to reflect stream-based output handling

Review Change Stack

@atmos-pro

atmos-pro Bot commented May 21, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@github-actions github-actions Bot added the size/m Medium size PR label May 21, 2026
@github-actions

github-actions Bot commented May 21, 2026 •

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@mergify

mergify Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

⚠️ The sha of the head commit of this PR conflicts with #2459. Mergify cannot evaluate rules on this PR. Once #2459 is merged or closed, Mergify will resume processing this PR. ⚠️

@codecov

codecov Bot commented May 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.93750% with 63 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.63%. Comparing base (ac6321c) to head (3467269).

Files with missing lines Patch % Lines
pkg/scheduler/scheduler.go 87.31% 26 Missing and 9 partials ⚠️
pkg/io/output.go 78.57% 6 Missing and 6 partials ⚠️
internal/exec/shell_utils.go 83.05% 7 Missing and 3 partials ⚠️
pkg/process/process.go 90.38% 4 Missing and 1 partial ⚠️
internal/exec/terraform_plan_diff.go 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2464      +/-   ##
==========================================
+ Coverage   78.59%   78.63%   +0.04%     
==========================================
  Files        1147     1150       +3     
  Lines      110578   110981     +403     
==========================================
+ Hits        86905    87270     +365     
- Misses      18845    18867      +22     
- Partials     4828     4844      +16     
Flag Coverage Δ
unittests 78.63% <85.93%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
errors/errors.go 100.00% <ø> (ø)
internal/exec/terraform_plan_diff.go 75.00% <80.00%> (+0.31%) ⬆️
pkg/process/process.go 90.38% <90.38%> (ø)
internal/exec/shell_utils.go 61.27% <83.05%> (+1.20%) ⬆️
pkg/io/output.go 78.57% <78.57%> (ø)
pkg/scheduler/scheduler.go 87.31% <87.31%> (ø)

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@osterman

Copy link
Copy Markdown
Member

CodeRabbit (@coderabbitai) is NodeStreams conventional terminology for the given use-case?

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Tip

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

🧩 Analysis chain

Let me check the code first.


🏁 Script executed:

#!/bin/bash
rg -n "NodeStreams" --type go -C 5

Length of output: 3851


@osterman: Short answer — not really established, but not wrong either. Here's the breakdown:

What NodeStreams does: Holds composed Stdout/Stderr writers for one DAG execution node, prefixed with a node ID, fanning out to Terminal, File, and Capture sinks. Scoped and well-defined.

Is the term conventional?

  • "Streams" for stdout/stderr is standard POSIX/Unix terminology — no issue there.
  • "Node" is the right DAG-layer abstraction given it takes a NodeID. Correct scoping.
  • "NodeStreams" as a compound, however, doesn't appear as a recognized pattern in Go DAG libraries (NodeOutput, TaskContext, TaskRunInfo are more common idioms in that space).
  • Minor: "NodeStreams" can superficially evoke the Node.js Streams API, which is unrelated.

Alternatives worth considering:

Name Rationale
NodeIO Concise; mirrors Go I/O conventions (bufio, stdio); avoids streams ambiguity
TaskStreams Consistent with TaskSpec naming already in pkg/process
NodeWriter Even more focused — it's writers, not full duplex streams

Of these, NodeIO feels most idiomatic for Go and avoids any association with reactive or Node.js-style streams. TaskStreams would also unify terminology with TaskSpec across pkg/process and pkg/io. Both would be clearer signals to a new contributor than NodeStreams.

@shirkevich
Mikhail Shirkov (shirkevich) force-pushed the codex/dag-process-io-foundation branch from 7e70c17 to 6a5036e Compare May 25, 2026 07:46
@shirkevich
Mikhail Shirkov (shirkevich) marked this pull request as ready for review May 26, 2026 18:07
@shirkevich
Mikhail Shirkov (shirkevich) requested a review from a team as a code owner May 26, 2026 18:07
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a subprocess Runner abstraction (pkg/process), composable per-scope output with line-prefixing and masking (pkg/io), and refactors shell and terraform execution to use injectable streams and contexts, routing shell execution through the new runner and updating related tests and docs.

Changes

Subprocess & Stream Refactor

Layer / File(s) Summary
Subprocess execution framework and error definitions
errors/errors.go, pkg/process/process.go, pkg/process/process_test.go
Adds ErrProcessStartFailed and ErrProcessWaitFailed sentinel errors. Introduces Runner interface, DefaultRunner implementation with TaskSpec (command, args, dir, env, streams, dry-run), Streams (stdin/stdout/stderr), and Result metadata. DefaultRunner.Run handles context cancellation, start/wait errors, exit-code extraction, and dry-run bypass. Helper tests exercise stream injection, exit-code propagation, timeouts, and failure cases.
Per-scope output composition with prefixing and masking
pkg/io/output.go, pkg/io/output_test.go
Defines Output, OutputSinks, OutputOptions for composable sink configuration. NewOutput defaults nil sinks to process stdout/stderr, then builds final writers via composeOutput with multi-sink fan-out using NewPrefixedWriter and MaskWriter. prefixedWriter applies thread-safe [<prefix>] prefixing across partial writes. Tests validate line prefixing, partial-write buffering, multi-sink propagation, and secret masking.
ExecuteShellCommand refactor with stream and context options
internal/exec/shell_utils.go, internal/exec/shell_utils_test.go
ExecuteShellCommand adds WithProcessStreams and WithProcessContext functional options. Stream handling now derives from process.OSStreams() with optional override, rebuilds masking/capture/redirect logic based on configured /dev/* targets, and executes via process.DefaultRunner.Run instead of direct exec.Cmd. Adds synchronizedWriter for /dev/stdout stderr redirection. Tests verify injected stream capture, exit-code preservation, and stderr-to-stdout redirection.
runTerraformShow integration with stdout override
internal/exec/terraform_plan_diff.go, internal/exec/terraform_plan_diff_test.go
Introduces internal terraformPlanDiffExecutor interface and function adapter. Refactors runTerraformShow to use runTerraformShowWithExecutor with injected executor; captures terraform show -json stdout into a bytes.Buffer via WithStdoutOverride instead of pipe/goroutine wiring.
Design documentation updates
docs/prd/dag-concurrent-execution.md
PRD updated to replace io.NewNodeStreams() with io.NewOutput() throughout; orchestration examples now assign output.Stdout/output.Stderr into spec.Streams and call runner.Run(ctx, spec). Documentation reflects execution-scoped output composition and prefixing as Phase 1 foundation.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant NewOutput
  participant DefaultRunner
  participant OSProcess
  Caller->>NewOutput: NewOutput(OutputOptions) -> Output{Stdout,Stderr}
  Caller->>DefaultRunner: Run(ctx, TaskSpec{Command,Args,Env,Streams: Output})
  DefaultRunner->>OSProcess: exec.CommandContext with wired Streams
  OSProcess-->>DefaultRunner: exit code / error / cancellation
  DefaultRunner-->>Caller: Result{ExitCode, Err, Started, Canceled}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • cloudposse/atmos#1382: Modifies terraform_plan_diff.go stdout-capture logic to mitigate pipe-based redirection, overlapping with this PR's refactor.
  • cloudposse/atmos#1660: Related changes to shell execution exit-code preservation; both PRs align on propagating accurate exit codes.
  • cloudposse/atmos#2194: Documentation and stream-injection design overlap with this PR's NewOutput() and runner wiring.

Suggested Labels

minor

Suggested Reviewers

  • osterman
  • aknysh
  • goruha

"A rabbit in a tiny hat,
hopped in to wire stdout and back,
he prefixed each line with a cheer,
masked secrets so none would appear,
and thumped his foot when the runner came back."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.08% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add process and I/O execution foundation' accurately captures the main objective of this PR, which introduces pkg/process and pkg/io packages as foundational components for DAG concurrent execution.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/dag-process-io-foundation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 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/exec/shell_utils_test.go`:
- Around line 197-200: Replace the hardcoded "/bin/sh" invocation in the tests
that call ExecuteShellCommand with a Go test helper process to keep tests
cross-platform: implement a TestHelperProcess helper in the test file that
checks an env var like GO_WANT_HELPER_PROCESS and writes the desired
stdout/stderr, and invoke it by calling os.Executable() (the current test
binary) with args "-test.run=TestHelperProcess" and any helper commands, plus
setting GO_WANT_HELPER_PROCESS=1 in the cmd.Env before passing the cmd to
ExecuteShellCommand; apply the same change to the other occurrence around the
ExecuteShellCommand call at the second location.

In `@internal/exec/shell_utils.go`:
- Around line 189-195: The /dev/stdout branch is wiring masked stderr to
stdoutTarget which bypasses the composed stdout capture (WithStdoutCapture);
change the MaskWriter call to wrap the composed stdout writer instead of
stdoutTarget so redirected stderr participates in stdout capture. In other
words, replace ioLayer.MaskWriter(stdoutTarget) with ioLayer.MaskWriter(stdout)
and keep the existing conditional that composes stderr with cfg.stderrCapture
(i.e., stderr = io.MultiWriter(maskedStderr, cfg.stderrCapture) or stderr =
maskedStderr) so stderr goes through the same captured stdout stream.

In `@pkg/process/process_test.go`:
- Around line 17-18: Replace the platform-specific shell fixtures and
runtime.GOOS checks by implementing a Go-native test helper process and using
os.Executable() (or inject a fake command runner) in the tests: remove hardcoded
"/bin/sh" and "sleep" calls in the tests and instead spawn the current test
binary with exec.Command(os.Executable(), "-test.run=TestHelperProcess", "--",
"<args>") and set an env var (e.g., "GO_WANT_HELPER_PROCESS=1") so that a
TestHelperProcess function in the same _test.go file handles the simulated
behavior (sleep, exit codes, stdout/stderr) in a cross-platform way;
alternatively, refactor the code under test to accept a CommandRunner interface
and inject a fake runner in tests, updating the tests that reference the shell
fixtures and the runtime.GOOS skip (the tests around the conditional at the top
and the blocks covering lines 22-24, 39-45, 56-65) to use the TestHelperProcess
or fake runner.

In `@pkg/process/process.go`:
- Around line 65-76: The Result.StartedAt is being set before the process
actually starts (and for dry runs), so update the logic in the function that
builds and runs the command (the code that constructs Result with Command/Args
and then calls cmd.Start()) to leave StartedAt zero until the process
successfully starts: do not set StartedAt when spec.DryRun is true, and only set
result.StartedAt = time.Now() immediately after a successful cmd.Start() return;
keep the defer that sets FinishedAt but ensure it runs regardless, and on
cmd.Start() failure return the Result with StartedAt zero and appropriate
ExitCode/Errors.
- Around line 88-105: Wrap the raw errors returned by cmd.Start() and cmd.Wait()
with the repo sentinel errors from errors/errors.go instead of returning them
raw: when cmd.Start() fails assign result.Err to a wrapped error using
fmt.Errorf or errors.Join with the static start sentinel (wrap the original err
with the sentinel) and set result.ExitCode = -1; when cmd.Wait() returns an
error wrap that error with the static wait sentinel (use errors.Join or
fmt.Errorf("%w", err)) before assigning result.Err, keep result.ExitCode =
exitCode(err), and if ctx.Err() is non-nil combine the wait sentinel, the wait
error and ctxErr using errors.Join so result.Err contains both the sentinel and
underlying causes; update assignments that touch result.Err, result.Started,
result.Canceled, and the handling around exitCode(err) accordingly (references:
cmd.Start(), cmd.Wait(), result.Err, result.ExitCode, result.Started,
result.Canceled, exitCode(err), ctx.Err()).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 68e9c1a2-c710-4aa2-8146-ef55b97400d1

📥 Commits

Reviewing files that changed from the base of the PR and between 754e6ce and 6a5036e.

📒 Files selected for processing (8)
  • internal/exec/shell_utils.go
  • internal/exec/shell_utils_test.go
  • internal/exec/terraform_plan_diff.go
  • internal/exec/terraform_plan_diff_test.go
  • pkg/io/node_streams.go
  • pkg/io/node_streams_test.go
  • pkg/process/process.go
  • pkg/process/process_test.go

Comment thread internal/exec/shell_utils_test.go Outdated
Comment thread internal/exec/shell_utils.go
Comment thread pkg/process/process_test.go Outdated
Comment thread pkg/process/process.go
Comment thread pkg/process/process.go

Copy link
Copy Markdown
Collaborator Author

CodeRabbit (@coderabbitai) review

Addressed the prior review threads in f9cb786:

  • replaced the new Unix shell fixtures with Go test-helper subprocesses
  • fixed /dev/stdout stderr redirection so it participates in stdout capture
  • set StartedAt only after successful process start
  • wrapped process start/wait failures with static sentinels
  • renamed NodeStreams to NodeOutput for the output-only abstraction

Validation:
rtk env GOCACHE=/private/tmp/atmos-gocache go test -p 1 ./pkg/process ./pkg/io ./internal/exec ./cmd/terraform

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor

@shirkevich: Got it. Kicking off a review of the updated changes.

✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
pkg/process/process.go (1)

63-110: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add perf tracking to the public runner method.

DefaultRunner.Run is a public, non-trivial function and currently misses the standard perf instrumentation on Line 63.

Suggested patch
 import (
 	"context"
 	"errors"
 	"fmt"
 	"io"
 	"os"
 	"os/exec"
 	"time"
 
 	errUtils "github.com/cloudposse/atmos/errors"
+	"github.com/cloudposse/atmos/pkg/perf"
 )
@@
 func (r DefaultRunner) Run(ctx context.Context, spec TaskSpec) (result Result) {
+	defer perf.Track(nil, "process.DefaultRunner.Run")()
+
 	if ctx == nil {
 		ctx = context.Background()
 	}

As per coding guidelines "Add defer perf.Track(atmosConfig, "pkg.FuncName")() plus blank line to all public functions, except trivial getters/setters, command constructors, simple factories, delegating functions, and pure validation/lookup functions."

🤖 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 `@pkg/process/process.go` around lines 63 - 110, Add perf tracking to
DefaultRunner.Run by inserting a defer perf.Track(atmosConfig,
"pkg.process.DefaultRunner.Run")() at the top of the function (immediately after
the ctx nil-check or as the first statement if you prefer), followed by a blank
line; update imports if necessary to include perf and atmosConfig usage. This
change targets the public method DefaultRunner.Run and must place the defer
before any heavy logic so the tracker wraps the entire function execution.
🤖 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 `@pkg/io/node_output_test.go`:
- Around line 54-55: The test currently ignores the return value of Initialize()
which can hide masking setup failures; change the test to assert initialization
succeeded before calling RegisterSecret by capturing the error from Initialize()
(e.g., err := Initialize()) and failing the test if err != nil (use t.Fatalf or
your test helper) so that Initialize() failures stop the test rather than
allowing RegisterSecret to run against an uninitialized state.

---

Outside diff comments:
In `@pkg/process/process.go`:
- Around line 63-110: Add perf tracking to DefaultRunner.Run by inserting a
defer perf.Track(atmosConfig, "pkg.process.DefaultRunner.Run")() at the top of
the function (immediately after the ctx nil-check or as the first statement if
you prefer), followed by a blank line; update imports if necessary to include
perf and atmosConfig usage. This change targets the public method
DefaultRunner.Run and must place the defer before any heavy logic so the tracker
wraps the entire function execution.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed797bb5-249a-4b8d-bf21-20a333d9f824

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5036e and f9cb786.

📒 Files selected for processing (8)
  • docs/prd/dag-concurrent-execution.md
  • errors/errors.go
  • internal/exec/shell_utils.go
  • internal/exec/shell_utils_test.go
  • pkg/io/node_output.go
  • pkg/io/node_output_test.go
  • pkg/process/process.go
  • pkg/process/process_test.go
✅ Files skipped from review due to trivial changes (1)
  • docs/prd/dag-concurrent-execution.md

Comment thread pkg/io/node_output_test.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
pkg/io/node_output_test.go (1)

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

Fail fast if masking initialization fails.

Ignoring the return from Initialize() can hide setup failures and make this test non-diagnostic.

Suggested patch
-	_ = Initialize()
+	require.NoError(t, Initialize())
 	RegisterSecret("secret-value")
🤖 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 `@pkg/io/node_output_test.go` around lines 54 - 55, The test currently ignores
the result of Initialize(); change it to capture and assert the error (e.g.,
call Initialize() and if it returns an error, fail the test) before calling
RegisterSecret("secret-value") so initialization failures cause an immediate
test failure—update the test to check Initialize()'s return and call
t.Fatalf/t.Fatal (or equivalent) on error.
🧹 Nitpick comments (1)
internal/exec/shell_utils_test.go (1)

217-282: ⚡ Quick win

Add a cancellation-path test for WithProcessContext.

This new option changes ExecuteShellCommand’s contract through the result.Canceled branch, but the added coverage only exercises success, exit code, and redirection. A helper mode that blocks until the context is canceled would pin that behavior down before more call sites depend on it.

As per coding guidelines: "Every new feature must include comprehensive unit tests targeting >80% code coverage for all packages".

🤖 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/exec/shell_utils_test.go` around lines 217 - 282, Add a unit test
that verifies ExecuteShellCommand honors WithProcessContext cancellation: create
a helper mode that blocks (e.g., extend shellHelperCommand with a
"block-until-cancel" behavior) then call ExecuteShellCommand with a cancellable
context passed via WithProcessContext(ctx) and
WithProcessStreams/WithStdoutCapture as needed, cancel the context from a
goroutine after a short delay, and assert the returned result indicates
cancellation (check result.Canceled or that the error is context.Canceled) and
that terminal/capture streams reflect any partial output; reference
ExecuteShellCommand, WithProcessContext, shellHelperCommand and result.Canceled
when adding the test.
🤖 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/exec/shell_utils_test.go`:
- Around line 278-281: The test currently asserts a fixed merged order
("stdoutstderr") which is unreliable; update the assertions to check that
terminalOut.String() and captureOut.String() each contain the substrings
"stdout" and "stderr" (use assertions like assert.Contains or strings.Contains)
rather than exact equality, and keep the existing require.NoError(t, err) and
assert.Empty(t, terminalErr.String()) checks; locate these in the test around
variables terminalOut, terminalErr, and captureOut to replace the equality
checks.

In `@internal/exec/terraform_plan_diff.go`:
- Line 29: The package-level mutable seam executeTerraformForPlanDiff should be
replaced with a dependency-injected interface: define an Executor interface
(e.g., type TerraformExecutor interface { ExecuteTerraform(args ...type)
(resultType, error) } matching the existing ExecuteTerraform signature), add a
field of that interface to the type that currently relies on
executeTerraformForPlanDiff (refer to executeTerraformForPlanDiff and
ExecuteTerraform), update the constructor to accept the TerraformExecutor and
use that field instead of the package var, and generate a mock for
TerraformExecutor with go.uber.org/mock/mockgen to replace tests that currently
mutate the package-global; update tests to inject the generated mock. Ensure
signatures and return types match the original ExecuteTerraform so call sites
compile.

In `@pkg/io/node_output.go`:
- Around line 70-77: NewPrefixedWriter can return a prefixedWriter wrapping a
nil stdio.Writer which will panic on write; ensure you normalize nil writers to
stdio.Discard. In NewPrefixedWriter, if w is nil set w = stdio.Discard before
any returns so both branches (prefix=="" and the prefixedWriter construction)
never return a nil writer; update references to NewPrefixedWriter and
prefixedWriter accordingly.

---

Duplicate comments:
In `@pkg/io/node_output_test.go`:
- Around line 54-55: The test currently ignores the result of Initialize();
change it to capture and assert the error (e.g., call Initialize() and if it
returns an error, fail the test) before calling RegisterSecret("secret-value")
so initialization failures cause an immediate test failure—update the test to
check Initialize()'s return and call t.Fatalf/t.Fatal (or equivalent) on error.

---

Nitpick comments:
In `@internal/exec/shell_utils_test.go`:
- Around line 217-282: Add a unit test that verifies ExecuteShellCommand honors
WithProcessContext cancellation: create a helper mode that blocks (e.g., extend
shellHelperCommand with a "block-until-cancel" behavior) then call
ExecuteShellCommand with a cancellable context passed via
WithProcessContext(ctx) and WithProcessStreams/WithStdoutCapture as needed,
cancel the context from a goroutine after a short delay, and assert the returned
result indicates cancellation (check result.Canceled or that the error is
context.Canceled) and that terminal/capture streams reflect any partial output;
reference ExecuteShellCommand, WithProcessContext, shellHelperCommand and
result.Canceled when adding the test.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79593cac-20b6-47a3-9c20-567989b80e8c

📥 Commits

Reviewing files that changed from the base of the PR and between 754e6ce and f9cb786.

📒 Files selected for processing (10)
  • docs/prd/dag-concurrent-execution.md
  • errors/errors.go
  • internal/exec/shell_utils.go
  • internal/exec/shell_utils_test.go
  • internal/exec/terraform_plan_diff.go
  • internal/exec/terraform_plan_diff_test.go
  • pkg/io/node_output.go
  • pkg/io/node_output_test.go
  • pkg/process/process.go
  • pkg/process/process_test.go

Comment thread internal/exec/shell_utils_test.go Outdated
Comment thread internal/exec/terraform_plan_diff.go Outdated
Comment thread pkg/io/output.go
@shirkevich

Copy link
Copy Markdown
Collaborator Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@shirkevich

Copy link
Copy Markdown
Collaborator Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented May 26, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 26, 2026
Comment thread pkg/io/node_output.go Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 27, 2026
@osterman Erik Osterman (Cloud Posse) (osterman) added the no-release Do not create a new release (wait for additional code changes) label May 27, 2026
@shirkevich

Copy link
Copy Markdown
Collaborator Author

CodeRabbit (@coderabbitai) review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

* add dag scheduler core

* Address CodeRabbit scheduler feedback

* Ensure scheduler aggregates non-success statuses
@github-actions github-actions Bot added size/l Large size PR and removed size/m Medium size PR labels May 29, 2026
@aknysh
Andriy Knysh (aknysh) merged commit 6259051 into main May 30, 2026
59 checks passed
@atmos-pro

atmos-pro Bot commented May 30, 2026 •

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@aknysh
Andriy Knysh (aknysh) deleted the codex/dag-process-io-foundation branch May 30, 2026 02:34
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown

These changes were released in v1.221.0-rc.2.

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

Labels

no-release Do not create a new release (wait for additional code changes) size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants