Repository navigation
feat: exec command - #60
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (59)
📒 Files selected for processing (13)
Disabled knowledge base sources:
WalkthroughAdds a new Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI (root)
participant ExecCmd as Exec Subcommand
participant Planner as Planner / Prepare
participant FS as RunArtifacts (FS)
participant Agent as ACP Client/Agent
participant Executor as ExecuteExec
participant Result as emitExecutionResult
CLI->>ExecCmd: invoke `compozy exec` (prompt | --prompt-file | stdin)
ExecCmd->>ExecCmd: resolveExecPromptSource() (exactly one)
ExecCmd->>Planner: Prepare(cfg with exec fields)
Planner->>FS: allocateRunArtifacts(workspaceRoot, runID) & write run.json
ExecCmd->>Executor: ExecuteExec(ctx, cfg)
Executor->>Agent: CreateSession or ResumeSession (based on run state)
Agent-->>Executor: session updates / streamed events
Executor->>FS: persist per-turn artifacts and events (if --persist)
Executor->>Result: buildExecutionResult(jobs, failures, runArtifacts)
Result->>FS: write result.json (JSON mode)
Result->>CLI: emit JSON/text to stdout/stderr per output mode
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/core/run/execution.go (1)
918-959:⚠️ Potential issue | 🟠 MajorKeep stderr fallback logs disabled while the UI is active.
emitHuman := cfg.humanOutputEnabled()is too broad here.handleNilExecution,handleSessionCancellation,handleSessionTimeout, andbuildFailureResultall write directly tostderr, so a TUI run now emits both UI events and raw fallback lines for the same failure/cancel path.Suggested fix
- emitHuman := cfg.humanOutputEnabled() + emitHuman := cfg.humanOutputEnabled() && !useUI🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/core/run/execution.go` around lines 918 - 959, The code currently sets emitHuman := cfg.humanOutputEnabled() unconditionally which allows stderr fallback logs while the UI is active; change emitHuman to respect the UI by setting it to cfg.humanOutputEnabled() && !useUI (or equivalent logic) so that when useUI is true, emitHuman is false; update the local variable used in the call to handleSessionTimeout/recordFailureWithContext and the eventual executeSessionAndResolve invocation so all failure/cancel paths (handleNilExecution, handleSessionCancellation, handleSessionTimeout, buildFailureResult) will not emit raw stderr fallback lines while the TUI is active.
🧹 Nitpick comments (5)
.gitignore (1)
17-18: Redundant node_modules patterns.
**/**/node_modulesis equivalent to**/node_modules, and**/node_modulesalready matches at the root level, making the separatenode_modules/entry unnecessary. Consider simplifying to a single pattern.♻️ Suggested simplification
-**/**/node_modules -node_modules/ +**/node_modules🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 17 - 18, The .gitignore contains redundant patterns for node_modules; remove the duplicate entries and keep a single concise pattern (e.g., "**/node_modules" or "node_modules/") and delete "**/**/node_modules" and the extra "node_modules/" so only one node_modules ignore pattern remains.internal/cli/workspace_config_test.go (1)
104-106:t.Parallel()with mutex-serialized directory changes is misleading.These tests are marked
t.Parallel()butchdirCLITestacquirescliWorkingDirMuwhich is held until cleanup. This means the tests will serialize on the mutex anyway, negating the parallelism benefit and potentially causing confusion about test behavior.Consider either removing
t.Parallel()from tests that usechdirCLITest, or restructuring to avoid shared working directory state.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/cli/workspace_config_test.go` around lines 104 - 106, The test TestApplyWorkspaceDefaultsUsesExecOverridesOverDefaults calls chdirCLITest which serializes via the cliWorkingDirMu mutex, so leaving t.Parallel() is misleading; either remove the t.Parallel() call from this test (and any other tests that call chdirCLITest) or refactor chdirCLITest to avoid global working-directory state (e.g., make it return an isolated temp dir and not use cliWorkingDirMu) so true parallelism is possible—refer to TestApplyWorkspaceDefaultsUsesExecOverridesOverDefaults, chdirCLITest, and cliWorkingDirMu to locate the code to change.internal/core/api_test.go (1)
32-43: Consider adding specific error message validation.The test verifies that validation fails, but doesn't assert on the error message content. Per coding guidelines, error assertions should be specific (e.g., using
ErrorContains).♻️ Suggested improvement
func TestConfigValidateRejectsExecModeWithoutPromptSource(t *testing.T) { t.Parallel() err := Config{ Mode: ModeExec, IDE: IDECodex, OutputFormat: OutputFormatText, }.Validate() if err == nil { t.Fatal("expected exec config without prompt source to fail validation") } + if !strings.Contains(err.Error(), "prompt") { + t.Fatalf("expected prompt-related error, got: %v", err) + } }As per coding guidelines: "MUST have specific error assertions (ErrorContains, ErrorAs)".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/core/api_test.go` around lines 32 - 43, Update the test TestConfigValidateRejectsExecModeWithoutPromptSource to assert the validation error message content rather than only checking nil: call Config{Mode: ModeExec, IDE: IDECodex, OutputFormat: OutputFormatText}.Validate(), verify err is non-nil and use an error assertion (e.g., ErrorContains) to check for the expected substring produced by Validate (for example mentioning missing prompt source or exec mode requirement), referencing Config.Validate and ModeExec to locate the validation logic.internal/cli/root_command_execution_test.go (1)
267-320: Same race condition concern with global I/O redirection.This helper shares the same pattern as
captureExecuteStreams- redirectingos.Stdout/os.Stderrglobally while the command may spawn goroutines that reference the original descriptors. Thecmd.SetOut/cmd.SetErrcalls (lines 289-290) are good but don't prevent code paths that directly write toos.Stdout/os.Stderr.Consider consolidating these helpers into a shared test utility to ensure consistent handling and make future fixes easier.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/cli/root_command_execution_test.go` around lines 267 - 320, The helper executeRootCommandCapturingProcessIO introduces a race by reassigning global os.Stdout/os.Stderr while commands may spawn goroutines; instead stop mutating os.Stdout/os.Stderr and rely on cmd.SetOut and cmd.SetErr (as used on lines with cmd.SetOut/cmd.SetErr) to capture output, or refactor code paths that write directly to the globals to accept io.Writer injection; also consolidate this helper with captureExecuteStreams into a shared test utility so the same safe capture strategy is used everywhere.internal/cli/root_test.go (1)
280-399: Use the repo’s requiredt.Run("Should...")shape for the new exec tests.These scenarios are all independent exec cases, but they’re added as standalone tests, and the one table-driven block still uses non-
Should...case names. Please fold the new exec coverage into table-drivent.Run("Should ...")cases so the additions match the enforced test contract.As per coding guidelines,
**/*_test.go:Use table-driven tests with subtests (t.Run) as the default patternandMUST use t.Run("Should...") pattern for ALL test cases.Also applies to: 491-605
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/cli/root_test.go` around lines 280 - 399, Convert the standalone tests TestExecHelpShowsExecFlagsOnly, TestExecHelpMatchesGolden, TestREADMEExecDocumentationMatchesCurrentContract, and TestActiveDocsAndHelpFixturesOmitLegacyArtifactRoot into a single table-driven test that uses t.Run("Should ...") subtests for each scenario (e.g., t.Run("Should show exec flags only", func(t *testing.T){...})), or wrap each existing test body in a t.Run call with a "Should ..." name; ensure all subtest names follow the "Should ..." pattern and keep the original assertions and helper calls (findCommand, executeRootCommand, mustCLITestDataPath, mustCLIRepoRootPath) unchanged, and apply the same conversion for the other exec-related tests mentioned (lines ~491-605) so every case uses t.Run("Should ...").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/cli/workspace_config_test.go`:
- Around line 243-264: The two tests in root_test.go
(TestStartCommandWrapsWorkspaceDefaultErrors and
TestSyncCommandWrapsWorkspaceRootErrors) call os.Chdir directly causing races
with workspace_config_test.go; change them to use the existing helper
chdirCLITest (which acquires cliWorkingDirMu) or wrap their os.Chdir calls with
cliWorkingDirMu.Lock()/Unlock() to serialize directory changes; update both
tests to call chdirCLITest(t, targetDir) (or add explicit mutex locking around
the Chdir and restore) so directory changes are protected by the cliWorkingDirMu
mutex.
In `@internal/core/run/result.go`:
- Around line 74-80: shutdownErr (which can include
awaitUIAfterCompletion()/shutdownUI() errors) is currently used to set
result.Error and mark runs as canceled, which hides actual job failures; change
the logic in result.go so teardown errors do not overwrite job outcomes: first
inspect failures (the failures slice) and set result.Error from failures[0] when
present, derive cancellation only from job-level cancellation flags or explicit
cancellation causes (not from shutdownErr), and store teardown/UI errors
separately (e.g., result.TeardownError or append to a result.Errors list)
instead of assigning shutdownErr.Error() to result.Error; apply the same fix to
the other similar block referenced (lines ~84-96) so teardown errors are
surfaced but don't mark successful job runs as canceled or hide earlier
failures.
---
Outside diff comments:
In `@internal/core/run/execution.go`:
- Around line 918-959: The code currently sets emitHuman :=
cfg.humanOutputEnabled() unconditionally which allows stderr fallback logs while
the UI is active; change emitHuman to respect the UI by setting it to
cfg.humanOutputEnabled() && !useUI (or equivalent logic) so that when useUI is
true, emitHuman is false; update the local variable used in the call to
handleSessionTimeout/recordFailureWithContext and the eventual
executeSessionAndResolve invocation so all failure/cancel paths
(handleNilExecution, handleSessionCancellation, handleSessionTimeout,
buildFailureResult) will not emit raw stderr fallback lines while the TUI is
active.
---
Nitpick comments:
In @.gitignore:
- Around line 17-18: The .gitignore contains redundant patterns for
node_modules; remove the duplicate entries and keep a single concise pattern
(e.g., "**/node_modules" or "node_modules/") and delete "**/**/node_modules" and
the extra "node_modules/" so only one node_modules ignore pattern remains.
In `@internal/cli/root_command_execution_test.go`:
- Around line 267-320: The helper executeRootCommandCapturingProcessIO
introduces a race by reassigning global os.Stdout/os.Stderr while commands may
spawn goroutines; instead stop mutating os.Stdout/os.Stderr and rely on
cmd.SetOut and cmd.SetErr (as used on lines with cmd.SetOut/cmd.SetErr) to
capture output, or refactor code paths that write directly to the globals to
accept io.Writer injection; also consolidate this helper with
captureExecuteStreams into a shared test utility so the same safe capture
strategy is used everywhere.
In `@internal/cli/root_test.go`:
- Around line 280-399: Convert the standalone tests
TestExecHelpShowsExecFlagsOnly, TestExecHelpMatchesGolden,
TestREADMEExecDocumentationMatchesCurrentContract, and
TestActiveDocsAndHelpFixturesOmitLegacyArtifactRoot into a single table-driven
test that uses t.Run("Should ...") subtests for each scenario (e.g.,
t.Run("Should show exec flags only", func(t *testing.T){...})), or wrap each
existing test body in a t.Run call with a "Should ..." name; ensure all subtest
names follow the "Should ..." pattern and keep the original assertions and
helper calls (findCommand, executeRootCommand, mustCLITestDataPath,
mustCLIRepoRootPath) unchanged, and apply the same conversion for the other
exec-related tests mentioned (lines ~491-605) so every case uses t.Run("Should
...").
In `@internal/cli/workspace_config_test.go`:
- Around line 104-106: The test
TestApplyWorkspaceDefaultsUsesExecOverridesOverDefaults calls chdirCLITest which
serializes via the cliWorkingDirMu mutex, so leaving t.Parallel() is misleading;
either remove the t.Parallel() call from this test (and any other tests that
call chdirCLITest) or refactor chdirCLITest to avoid global working-directory
state (e.g., make it return an isolated temp dir and not use cliWorkingDirMu) so
true parallelism is possible—refer to
TestApplyWorkspaceDefaultsUsesExecOverridesOverDefaults, chdirCLITest, and
cliWorkingDirMu to locate the code to change.
In `@internal/core/api_test.go`:
- Around line 32-43: Update the test
TestConfigValidateRejectsExecModeWithoutPromptSource to assert the validation
error message content rather than only checking nil: call Config{Mode: ModeExec,
IDE: IDECodex, OutputFormat: OutputFormatText}.Validate(), verify err is non-nil
and use an error assertion (e.g., ErrorContains) to check for the expected
substring produced by Validate (for example mentioning missing prompt source or
exec mode requirement), referencing Config.Validate and ModeExec to locate the
validation logic.
🪄 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: c56f0915-dc48-4b24-82e5-360aba925960
⛔ Files ignored due to path filters (92)
.agents/skills/council/SKILL.mdis excluded by!**/*.md,!.agents/**.agents/skills/council/assets/synthesis-template.mdis excluded by!**/*.md,!.agents/**.agents/skills/council/references/archetypes.mdis excluded by!**/*.md,!.agents/**.agents/skills/council/references/debate-protocols.mdis excluded by!**/*.md,!.agents/**.claude/agents/architect-advisor.mdis excluded by!**/*.md,!.claude/**.claude/agents/devils-advocate.mdis excluded by!**/*.md,!.claude/**.claude/agents/pragmatic-engineer.mdis excluded by!**/*.md,!.claude/**.claude/agents/product-mind.mdis excluded by!**/*.md,!.claude/**.claude/agents/security-advocate.mdis excluded by!**/*.md,!.claude/**.claude/agents/the-thinker.mdis excluded by!**/*.md,!.claude/**.commitlintrc.yamlis excluded by!**/*.yaml.compozy/tasks/_archived/20260405-165117-acp-integration/_meta.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/_tasks.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/_techspec.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-001.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-002.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-003.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-004.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-005.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/memory/MEMORY.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_01.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_02.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_03.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/_meta.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_001.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_002.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_003.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_004.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_005.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_006.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_007.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_008.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_009.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_010.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_011.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_012.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_013.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_014.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_015.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_016.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_017.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_018.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/task_01.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/task_02.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-acp-integration/task_03.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/_meta.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/_tasks.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/_techspec.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-001.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-002.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-003.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-004.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/MEMORY.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_01.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_02.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_03.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_04.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_05.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_06.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/_meta.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/issue_001.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/issue_002.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_01.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_02.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_03.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_04.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_05.mdis excluded by!**/*.md.compozy/tasks/_archived/20260405-165117-tasks-changes/task_06.mdis excluded by!**/*.md.compozy/tasks/exec-command/_meta.mdis excluded by!**/*.md.compozy/tasks/exec-command/_tasks.mdis excluded by!**/*.md.compozy/tasks/exec-command/_techspec.mdis excluded by!**/*.md.compozy/tasks/exec-command/adrs/adr-001.mdis excluded by!**/*.md.compozy/tasks/exec-command/adrs/adr-002.mdis excluded by!**/*.md.compozy/tasks/exec-command/adrs/adr-003.mdis excluded by!**/*.md.compozy/tasks/exec-command/memory/MEMORY.mdis excluded by!**/*.md.compozy/tasks/exec-command/memory/task_01.mdis excluded by!**/*.md.compozy/tasks/exec-command/memory/task_02.mdis excluded by!**/*.md.compozy/tasks/exec-command/memory/task_03.mdis excluded by!**/*.md.compozy/tasks/exec-command/memory/task_04.mdis excluded by!**/*.md.compozy/tasks/exec-command/reviews-001/_meta.mdis excluded by!**/*.md.compozy/tasks/exec-command/reviews-001/issue_001.mdis excluded by!**/*.md.compozy/tasks/exec-command/reviews-001/issue_002.mdis excluded by!**/*.md.compozy/tasks/exec-command/reviews-001/issue_003.mdis excluded by!**/*.md.compozy/tasks/exec-command/task_01.mdis excluded by!**/*.md.compozy/tasks/exec-command/task_02.mdis excluded by!**/*.md.compozy/tasks/exec-command/task_03.mdis excluded by!**/*.md.compozy/tasks/exec-command/task_04.mdis excluded by!**/*.mdREADME.mdis excluded by!**/*.mdbun.lockis excluded by!**/*.lockimgs/screenshot.pngis excluded by!**/*.pngpackage.jsonis excluded by!**/*.jsonskills-lock.jsonis excluded by!**/*.json
📒 Files selected for processing (30)
.gitignore.husky/commit-msginternal/cli/migrate_command_test.gointernal/cli/root.gointernal/cli/root_command_execution_test.gointernal/cli/root_test.gointernal/cli/testdata/exec_help.goldeninternal/cli/workspace_config.gointernal/cli/workspace_config_test.gointernal/core/agent/registry.gointernal/core/agent/registry_test.gointernal/core/api.gointernal/core/api_test.gointernal/core/model/model.gointernal/core/model/model_test.gointernal/core/plan/input.gointernal/core/plan/prepare.gointernal/core/plan/prepare_test.gointernal/core/run/command_io.gointernal/core/run/execution.gointernal/core/run/execution_acp_integration_test.gointernal/core/run/execution_acp_test.gointernal/core/run/logging.gointernal/core/run/logging_test.gointernal/core/run/result.gointernal/core/run/result_test.gointernal/core/run/types.gointernal/core/workspace/config.gointernal/core/workspace/config_test.gotest/public_api_test.go
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
internal/core/run/execution_acp_test.go (1)
691-705: Cover bothemitHumanbranches in this test.Line 696 hard-codes
true, so this still only validates the setup-failure payload and never locks in the new suppression behavior. A smallt.Runmatrix foremitHuman=true/falsewith captured stderr would make the signature change observable. As per coding guidelines, "Use table-driven tests with subtests (t.Run) as the default pattern".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/core/run/execution_acp_test.go` around lines 691 - 705, The test TestHandleNilExecutionReturnsSetupFailure currently only calls handleNilExecution with emitHuman hard-coded true, so add a table-driven subtest matrix using t.Run to exercise both emitHuman=true and emitHuman=false; for each subtest call handleNilExecution(&job{...}, 0, emitHuman) and capture/inspect stderr (or the logger output) to assert the new suppression behavior in addition to the existing checks on result.status and result.failure.err; update assertions inside each subtest to validate both the setup-failure payload and the presence or absence of the human-readable output depending on the emitHuman value so both branches of the function are covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/cli/root_test.go`:
- Around line 1332-1335: The wrapper executeRootCommand currently returns stdout
+ stderr which can reorder interleaved output; change it to preserve emission
order by capturing both streams into a single unified writer when calling
executeRootCommandWithIO (e.g., pass a single io.Writer that is used for both
stdout and stderr) so the returned string reflects actual emission order; apply
the same unified-stream change to the other wrapper variants that call
executeRootCommandWithIO in this file (the functions around the same area).
- Around line 559-563: The subtests in the table-driven tests call
t.Run(tc.name, ...) with names like "positional prompt" that don't follow the
required "Should..." convention; update the test case names in the cases table
(the elements used by the loop over cases) so each tc.name begins with "Should
..." (e.g., "Should handle positional prompt"), or replace the t.Run call to
format a "Should ..." prefix (t.Run(fmt.Sprintf("Should %s", tc.name), ...)) so
all subtest names conform to the t.Run("Should...") pattern referenced by the
test harness; locate the table of test cases and the t.Run invocation inside the
loop to apply the change (cases, tc.name, t.Run).
In `@internal/core/run/execution.go`:
- Around line 166-172: The shutdown fallback writes currently only check
humanOutputEnabled() and therefore still print to stderr during interactive UI
runs; update the drain/force/completion fallback branches in execution.go to
first check c.execCtx.cfg.humanOutputEnabled() before writing to os.Stderr
(apply same guard used around the existing UI path), remove or neutralize
wording that implies a graceful-only shutdown (change the completion message in
the final branch to a neutral statement like "Controller shutdown complete" so
it remains accurate after beginForce()), and ensure that publishShutdownStatus()
remains the primary mechanism for UI state updates; apply these same guarded
changes to the other fallback write sites corresponding to the ranges around the
existing branches (the ones referenced near lines 208-215 and 239-241).
In `@internal/core/run/result_test.go`:
- Around line 11-158: Add a new test that exercises
deriveRunStatus()/buildExecutionResult()'s canceled-precedence branch: create a
runArtifacts/config like the other tests, build a jobs slice with a job having
status runStatusCanceled (use the job struct and runArtifacts.JobsDir for
paths), pass non-nil []failInfo (e.g. failInfo{{err: errors.New("job failed")}})
and a teardown error to buildExecutionResult, then assert that result.Status ==
runStatusCanceled (not failed) while preserving result.Error and
result.TeardownError as appropriate; place the test alongside the existing
TestBuildExecutionResult... tests to cover the highest-priority canceled branch.
---
Nitpick comments:
In `@internal/core/run/execution_acp_test.go`:
- Around line 691-705: The test TestHandleNilExecutionReturnsSetupFailure
currently only calls handleNilExecution with emitHuman hard-coded true, so add a
table-driven subtest matrix using t.Run to exercise both emitHuman=true and
emitHuman=false; for each subtest call handleNilExecution(&job{...}, 0,
emitHuman) and capture/inspect stderr (or the logger output) to assert the new
suppression behavior in addition to the existing checks on result.status and
result.failure.err; update assertions inside each subtest to validate both the
setup-failure payload and the presence or absence of the human-readable output
depending on the emitHuman value so both branches of the function are covered.
🪄 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: 8e249bdf-4e41-4b9b-8bb4-24f73a5a2444
📒 Files selected for processing (7)
internal/cli/root_test.gointernal/core/model/model.gointernal/core/model/model_test.gointernal/core/run/execution.gointernal/core/run/execution_acp_test.gointernal/core/run/result.gointernal/core/run/result_test.go
✅ Files skipped from review due to trivial changes (1)
- internal/core/model/model_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/core/run/result.go
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/core/workspace/config.go (1)
169-188:⚠️ Potential issue | 🟠 MajorValidate the merged exec output format, not only
[exec].output_format.This misses configs like
[defaults] output_format = "json"with[exec] tui = true.ProjectConfig.Validate()accepts that file, but the effective exec config is still invalid and later fails at runtime once defaults are applied. Folddefaults.output_formatinto this check when the exec override is unset.Also applies to: 236-247
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/core/workspace/config.go` around lines 169 - 188, ProjectConfig.Validate currently only validates validateExec(cfg.Exec) and misses cases where output_format is set in Defaults but Exec.output_format is unset (e.g., defaults.output_format="json" with exec.tui=true), causing invalid effective exec config at runtime; update ProjectConfig.Validate to compute the effective exec config by merging cfg.Defaults (specifically Defaults.OutputFormat) into cfg.Exec when Exec.OutputFormat is empty and then pass that merged exec struct into validateExec (also apply the same merge logic in the other validation block referenced around validateExec usage at the later location mentioned).
🧹 Nitpick comments (2)
internal/cli/root.go (1)
777-779: Preferslices.Equaloverreflect.DeepEqualhere.Both sides are normalized
[]strings, so reflection is unnecessary for this comparison and makes the compatibility check heavier than it needs to be.As per coding guidelines,
Do not use reflection without performance justification.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/cli/root.go` around lines 777 - 779, Replace the reflect.DeepEqual call with slices.Equal for the normalized []string comparison: change reflect.DeepEqual(core.NormalizeAddDirs(cfg.AddDirs), core.NormalizeAddDirs(record.AddDirs)) to slices.Equal(...) and ensure the "slices" package is imported; keep the existing cmd.Flags().Changed("add-dir") check and use core.NormalizeAddDirs on both cfg.AddDirs and record.AddDirs as before to perform the equality check.internal/core/run/exec_flow.go (1)
791-797:emithas redundant dead conditional logic.When
s == nil || s.events == nil, both branches returnnil; the nested condition can be removed for clarity.💡 Proposed cleanup
func (s *execRunState) emit(event execEvent) error { if s == nil || s.events == nil { - if strings.TrimSpace(event.Output) == "" || event.Type != "run.failed" { - return nil - } return nil } return s.events.Write(event) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@internal/core/run/exec_flow.go` around lines 791 - 797, The early conditional in execRunState.emit has redundant dead logic: when s == nil || s.events == nil the function always returns nil but currently contains an unnecessary nested strings.TrimSpace(event.Output) and event.Type check; simplify by making the top-level guard (in method emit on type execRunState) immediately return nil when s == nil || s.events == nil and remove the inner conditional that checks event.Output and event.Type, leaving normal behavior for the rest of the method that uses s.events.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@internal/cli/root.go`:
- Around line 811-814: Avoid calling readPromptFromCommandInput(cmd.InOrStdin())
eagerly; only probe stdin after explicit prompt sources (the [prompt] value and
--prompt-file) have been ruled out. Change the logic in root command setup so
you first check whether a prompt value or prompt-file flag is set, and if
neither is present then detect whether stdin is a non-interactive pipe/tty (use
os.Stdin.Stat to check for a pipe) and perform a non-blocking readiness check
before calling readPromptFromCommandInput; if stdin is not ready or is a
terminal, skip calling readPromptFromCommandInput to prevent blocking. Ensure
the same change is applied to the other block that currently calls
readPromptFromCommandInput (the code around the 863–885 region) so both places
only read stdin when explicitly needed.
In `@internal/core/agent/registry.go`:
- Around line 341-356: The validation in validateRuntimeOutputFormat incorrectly
rejects the zero-value output format; treat the empty string as equivalent to
model.OutputFormatText by accepting "" in the allowed cases (or normalize
cfg.OutputFormat = model.OutputFormatText before validating). Update
validateRuntimeOutputFormat (referencing cfg.OutputFormat and cfg.Mode) so "" is
considered valid and the existing exec-mode check continues to compare against
model.OutputFormatText (or the normalized value).
In `@internal/core/run/exec_flow_integration_test.go`:
- Around line 298-333: Rename the subtest names in the table-driven test to
follow the "Should..." convention: update the t.Run labels for each case
(currently values like "keeps agent message chunks", "drops plan updates", etc.)
to "Should keep agent message chunks", "Should drop plan updates", "Should keep
unknown completed updates", and "Should drop unknown running updates" so they
match the repo's enforced t.Run("Should...") pattern; ensure these updated
labels are used in the loop that calls t.Run for the tests exercising
shouldEmitLeanSessionUpdate.
In `@internal/core/run/exec_flow.go`:
- Around line 1054-1056: The run ID generator buildExecRunID currently uses
time.Format("20060102-150405-000000000") so the trailing zeros are literal and
IDs collide within the same second; update buildExecRunID to include fractional
seconds by using the correct Go layout (e.g., "20060102-150405.000000000") or
alternatively append time.Now().UTC().UnixNano() (or a short hex/base36 of it)
to the ID so multiple IDs generated in the same second are unique; modify the
buildExecRunID function and its fmt.Sprintf call accordingly.
- Around line 405-437: The goroutine in streamExecSession can leak because it
never observes context cancellation; change streamExecSession to accept the
attempt context (e.g., streamExecSession(ctx context.Context, execution
*sessionExecution, state *execRunState)), and inside the goroutine replace the
range over execution.session.Updates() with an explicit loop that selects on
ctx.Done() and a receive from execution.session.Updates(); when ctx is done
return (and send ctx.Err() on streamErrCh), otherwise handle the update as
before (calling execution.handler.HandleUpdate and state.emitSessionUpdate).
Update the call site to pass attemptCtx into streamExecSession so the goroutine
stops promptly when attemptCtx is canceled.
In `@internal/core/run/execution_acp_integration_test.go`:
- Around line 1137-1182: The captureExecuteStreams helper blocks if fn() writes
more than the OS pipe buffer because stdout/stderr are only read after fn
returns; fix by starting background goroutines that continuously read from
stdoutRead and stderrRead into byte buffers (or io.ReadAll) before calling fn(),
use channels to return the read results (bytes and any read error), then call
fn(), close stdoutWrite/stderrWrite, and wait for the reader goroutines to
finish and send their results; reference the symbols captureExecuteStreams,
stdoutRead, stdoutWrite, stderrRead, stderrWrite and runErr to locate where to
spawn the readers, pass their outputs back, and return the collected
stdout/stderr strings along with runErr.
---
Outside diff comments:
In `@internal/core/workspace/config.go`:
- Around line 169-188: ProjectConfig.Validate currently only validates
validateExec(cfg.Exec) and misses cases where output_format is set in Defaults
but Exec.output_format is unset (e.g., defaults.output_format="json" with
exec.tui=true), causing invalid effective exec config at runtime; update
ProjectConfig.Validate to compute the effective exec config by merging
cfg.Defaults (specifically Defaults.OutputFormat) into cfg.Exec when
Exec.OutputFormat is empty and then pass that merged exec struct into
validateExec (also apply the same merge logic in the other validation block
referenced around validateExec usage at the later location mentioned).
---
Nitpick comments:
In `@internal/cli/root.go`:
- Around line 777-779: Replace the reflect.DeepEqual call with slices.Equal for
the normalized []string comparison: change
reflect.DeepEqual(core.NormalizeAddDirs(cfg.AddDirs),
core.NormalizeAddDirs(record.AddDirs)) to slices.Equal(...) and ensure the
"slices" package is imported; keep the existing cmd.Flags().Changed("add-dir")
check and use core.NormalizeAddDirs on both cfg.AddDirs and record.AddDirs as
before to perform the equality check.
In `@internal/core/run/exec_flow.go`:
- Around line 791-797: The early conditional in execRunState.emit has redundant
dead logic: when s == nil || s.events == nil the function always returns nil but
currently contains an unnecessary nested strings.TrimSpace(event.Output) and
event.Type check; simplify by making the top-level guard (in method emit on type
execRunState) immediately return nil when s == nil || s.events == nil and remove
the inner conditional that checks event.Output and event.Type, leaving normal
behavior for the rest of the method that uses s.events.
🪄 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: 5c8e91c6-0cdb-42eb-88bb-f63debf3665f
⛔ Files ignored due to path filters (1)
README.mdis excluded by!**/*.md
📒 Files selected for processing (26)
internal/cli/root.gointernal/cli/root_command_execution_test.gointernal/cli/root_test.gointernal/cli/testdata/exec_help.goldeninternal/cli/workspace_config.gointernal/cli/workspace_config_test.gointernal/core/agent/client.gointernal/core/agent/client_test.gointernal/core/agent/registry.gointernal/core/agent/registry_test.gointernal/core/agent/session.gointernal/core/agent/session_helpers_test.gointernal/core/api.gointernal/core/api_test.gointernal/core/model/model.gointernal/core/run/command_io.gointernal/core/run/exec_flow.gointernal/core/run/exec_flow_integration_test.gointernal/core/run/execution.gointernal/core/run/execution_acp_integration_test.gointernal/core/run/execution_acp_test.gointernal/core/run/logging.gointernal/core/run/runtime_logger.gointernal/core/run/types.gointernal/core/workspace/config.gointernal/core/workspace/config_test.go
✅ Files skipped from review due to trivial changes (2)
- internal/cli/testdata/exec_help.golden
- internal/core/api_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
- internal/core/run/logging.go
- internal/core/run/types.go
- internal/cli/root_command_execution_test.go
- internal/core/agent/registry_test.go
- internal/core/workspace/config_test.go
- internal/cli/root_test.go
- internal/core/run/command_io.go
Summary by CodeRabbit
New Features
compozy execfor ad-hoc prompts (positional, --prompt-file, or stdin).--persist/--run-id; artifacts under .compozy/runs// (metadata, events, per-turn files).Bug Fixes
Chores