Skip to content

feat: exec command - #60

Merged
pedronauck merged 11 commits into
mainfrom
pn/exec
Apr 5, 2026
Merged

pedronauck merged 11 commits into
mainfrom
pn/exec

Conversation

@pedronauck

@pedronauck pedronauck commented Apr 5, 2026 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added compozy exec for ad-hoc prompts (positional, --prompt-file, or stdin).
    • New output formats: text, json, raw-json; JSON modes emit structured JSONL events and a persisted JSON result.
    • Persist and resume runs with --persist / --run-id; artifacts under .compozy/runs// (metadata, events, per-turn files).
    • Exec flags: format, verbose, tui, persist, run-id, prompt-file; updated help/examples.
  • Bug Fixes

    • Exec input validation requires exactly one prompt source and reports clear JSON-aware failures.
  • Chores

    • Commit message lint hook and expanded .gitignore rules.

@pedronauck pedronauck self-assigned this Apr 5, 2026
@coderabbitai

coderabbitai Bot commented Apr 5, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3713ccf4-1af5-4d65-a7b7-0dd2e8fa1918

📥 Commits

Reviewing files that changed from the base of the PR and between 54c80b4 and 1a1c771.

⛔ Files ignored due to path filters (59)
  • .compozy/tasks/_archived/20260405-233422-exec-command/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/_tasks.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/_techspec.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/adrs/adr-001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/adrs/adr-002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/adrs/adr-003.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/memory/MEMORY.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/memory/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/memory/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/memory/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/memory/task_04.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/reviews-001/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/reviews-001/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-233422-exec-command/task_04.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/_meta.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/_tasks.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/_techspec.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-001.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-002.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-003.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-004.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-005.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/adrs/adr-006.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/MEMORY.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_01.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_02.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_03.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_04.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_05.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_06.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_07.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/memory/task_08.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_01.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_02.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_03.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_04.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_05.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_06.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_07.md is excluded by !**/*.md
  • .compozy/tasks/engine-kernel/task_08.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/_summary.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/001-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/002-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/003-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/004-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/005-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/006-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/007-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/008-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/009-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/010-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/issues/011-issue.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/outside/002-outside_01_internal_core_run_execution.go.md is excluded by !**/*.md
  • ai-docs/reviews-pr-60/outside/004-outside_01_internal_core_workspace_config.go.md is excluded by !**/*.md
📒 Files selected for processing (13)
  • internal/cli/root.go
  • internal/cli/root_test.go
  • internal/core/agent/registry.go
  • internal/core/agent/registry_test.go
  • internal/core/run/exec_flow.go
  • internal/core/run/exec_flow_integration_test.go
  • internal/core/run/exec_flow_test.go
  • internal/core/run/execution.go
  • internal/core/run/execution_acp_integration_test.go
  • internal/core/run/execution_ui_test.go
  • internal/core/run/result_test.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

Adds a new compozy exec command and exec-mode runtime including prompt-source resolution (positional/file/stdin), run-scoped artifacts under .compozy/runs, JSON/text/raw-json output modes, persisted/resumable exec runs and events, exec-specific validation, session resume support, and extensive CLI/core tests.

Changes

Cohort / File(s) Summary
CLI: exec command & support
internal/cli/root.go, internal/cli/root_command_execution_test.go, internal/cli/root_test.go, internal/cli/testdata/exec_help.golden, internal/cli/migrate_command_test.go, internal/cli/workspace_config.go, internal/cli/workspace_config_test.go
Added compozy exec subcommand, flags (format/verbose/tui/persist/run-id/ide/model/reasoning-effort/add-dir), prompt-source resolution, workspace defaults mapping for exec, help golden, and many exec-related CLI tests and helpers (IO capture, chdir mutex).
Core API & Model
internal/core/api.go, internal/core/model/model.go, internal/core/api_test.go, internal/core/model/model_test.go
Introduced ModeExec and OutputFormat (text/json/raw-json); extended exported Config/RuntimeConfig with exec/prompt/runtime fields; added runs directory helpers and RunArtifacts/JobArtifacts with sanitization and defaults.
Planning & Preparation
internal/core/plan/input.go, internal/core/plan/prepare.go, internal/core/plan/prepare_test.go
Added exec-specific input/prompt resolution and prepareExec; replaced prompt-root usage with RunArtifacts allocation, run-id/metadata creation, updated prepareJobs/buildBatchJob signatures and tests.
Execution flow & run artifacts
internal/core/run/exec_flow.go, internal/core/run/exec_flow_test.go, internal/core/run/exec_flow_integration_test.go, internal/core/run/execution.go, internal/core/run/execution_acp_integration_test.go, internal/core/run/execution_acp_test.go, internal/core/run/execution_ui_test.go
New end-to-end exec flow ExecuteExec with persisted Run JSON, per-turn artifacts/events JSONL, lean/raw JSON stdout streaming, retry/resume semantics, UI integration, and many integration/unit tests covering output modes and resume.
Result emission & types
internal/core/run/result.go, internal/core/run/result_test.go, internal/core/run/types.go
Added execution result structures, buildExecutionResult/emitExecutionResult, persisted result.json behavior (JSON mode), expanded config and job runtime state tracking, and unit tests.
Logging, session updates & IO
internal/core/run/logging.go, internal/core/run/logging_test.go, internal/core/run/command_io.go, internal/core/run/runtime_logger.go
Session update handler snapshotting, job usage aggregation, createLogWriters emitHuman behavior, runtime logger selection changes, UI/human output gating, and related test updates.
Agent client & session resume
internal/core/agent/client.go, internal/core/agent/session.go, internal/core/agent/registry.go, internal/core/agent/*_test.go, internal/core/agent/session_helpers_test.go
Added ResumeSession and SupportsLoadSession API, ResumeSessionRequest, session identity/resume semantics, load-session wiring, agent metadata extraction, registry validation pipeline updated for exec mode, and tests supporting load/resume.
Workspace config & validation
internal/core/workspace/config.go, internal/core/workspace/config_test.go
Introduced ExecConfig and unified RuntimeOverrides (including output_format), added validation for output formats and exec-specific cross-field rules (e.g., tui incompatible with JSON outputs).
Miscellaneous infra & repo hygiene
.husky/commit-msg, .gitignore
Added commit-msg Husky hook to run commitlint and extended .gitignore to ignore node_modules and .compozy/runs.
Tests & helpers
multiple internal/*/_test.go, test/public_api_test.go
Widespread test additions/updates for exec mode, IO capture helpers, persisted-run seeding, ACP helper extensions, and updates to many existing tests to new signatures and behaviors.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title "feat: exec command" is concise and clearly summarizes the main change—the addition of a new exec command to the codebase. It directly reflects the primary objective evident throughout the changeset.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pn/exec

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: 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 | 🟠 Major

Keep stderr fallback logs disabled while the UI is active.

emitHuman := cfg.humanOutputEnabled() is too broad here. handleNilExecution, handleSessionCancellation, handleSessionTimeout, and buildFailureResult all write directly to stderr, 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_modules is equivalent to **/node_modules, and **/node_modules already matches at the root level, making the separate node_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() but chdirCLITest acquires cliWorkingDirMu which 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 use chdirCLITest, 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 - redirecting os.Stdout/os.Stderr globally while the command may spawn goroutines that reference the original descriptors. The cmd.SetOut/cmd.SetErr calls (lines 289-290) are good but don't prevent code paths that directly write to os.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 required t.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-driven t.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 pattern and MUST 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5d27312 and 8dd463a.

⛔ Files ignored due to path filters (92)
  • .agents/skills/council/SKILL.md is excluded by !**/*.md, !.agents/**
  • .agents/skills/council/assets/synthesis-template.md is excluded by !**/*.md, !.agents/**
  • .agents/skills/council/references/archetypes.md is excluded by !**/*.md, !.agents/**
  • .agents/skills/council/references/debate-protocols.md is excluded by !**/*.md, !.agents/**
  • .claude/agents/architect-advisor.md is excluded by !**/*.md, !.claude/**
  • .claude/agents/devils-advocate.md is excluded by !**/*.md, !.claude/**
  • .claude/agents/pragmatic-engineer.md is excluded by !**/*.md, !.claude/**
  • .claude/agents/product-mind.md is excluded by !**/*.md, !.claude/**
  • .claude/agents/security-advocate.md is excluded by !**/*.md, !.claude/**
  • .claude/agents/the-thinker.md is excluded by !**/*.md, !.claude/**
  • .commitlintrc.yaml is excluded by !**/*.yaml
  • .compozy/tasks/_archived/20260405-165117-acp-integration/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/_tasks.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/_techspec.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-003.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-004.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/adrs/adr-005.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/memory/MEMORY.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/memory/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_004.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_005.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_006.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_007.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_008.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_009.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_010.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_011.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_012.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_013.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_014.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_015.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_016.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_017.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/reviews-001/issue_018.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-acp-integration/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/_tasks.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/_techspec.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-003.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/adrs/adr-004.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/MEMORY.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_04.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_05.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/memory/task_06.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/_meta.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_01.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_02.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_03.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_04.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_05.md is excluded by !**/*.md
  • .compozy/tasks/_archived/20260405-165117-tasks-changes/task_06.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/_meta.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/_tasks.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/_techspec.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/adrs/adr-001.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/adrs/adr-002.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/adrs/adr-003.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/memory/MEMORY.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/memory/task_01.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/memory/task_02.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/memory/task_03.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/memory/task_04.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/reviews-001/_meta.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/reviews-001/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/task_01.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/task_02.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/task_03.md is excluded by !**/*.md
  • .compozy/tasks/exec-command/task_04.md is excluded by !**/*.md
  • README.md is excluded by !**/*.md
  • bun.lock is excluded by !**/*.lock
  • imgs/screenshot.png is excluded by !**/*.png
  • package.json is excluded by !**/*.json
  • skills-lock.json is excluded by !**/*.json
📒 Files selected for processing (30)
  • .gitignore
  • .husky/commit-msg
  • internal/cli/migrate_command_test.go
  • internal/cli/root.go
  • internal/cli/root_command_execution_test.go
  • internal/cli/root_test.go
  • internal/cli/testdata/exec_help.golden
  • internal/cli/workspace_config.go
  • internal/cli/workspace_config_test.go
  • internal/core/agent/registry.go
  • internal/core/agent/registry_test.go
  • internal/core/api.go
  • internal/core/api_test.go
  • internal/core/model/model.go
  • internal/core/model/model_test.go
  • internal/core/plan/input.go
  • internal/core/plan/prepare.go
  • internal/core/plan/prepare_test.go
  • internal/core/run/command_io.go
  • internal/core/run/execution.go
  • internal/core/run/execution_acp_integration_test.go
  • internal/core/run/execution_acp_test.go
  • internal/core/run/logging.go
  • internal/core/run/logging_test.go
  • internal/core/run/result.go
  • internal/core/run/result_test.go
  • internal/core/run/types.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • test/public_api_test.go

Comment thread internal/cli/workspace_config_test.go
Comment thread internal/core/model/model.go
Comment thread internal/core/run/result.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: 4

🧹 Nitpick comments (1)
internal/core/run/execution_acp_test.go (1)

691-705: Cover both emitHuman branches 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 small t.Run matrix for emitHuman=true/false with 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

📥 Commits

Reviewing files that changed from the base of the PR and between 8dd463a and 1d99df4.

📒 Files selected for processing (7)
  • internal/cli/root_test.go
  • internal/core/model/model.go
  • internal/core/model/model_test.go
  • internal/core/run/execution.go
  • internal/core/run/execution_acp_test.go
  • internal/core/run/result.go
  • internal/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

Comment thread internal/cli/root_test.go
Comment thread internal/cli/root_test.go
Comment thread internal/core/run/execution.go Outdated
Comment thread internal/core/run/result_test.go

@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: 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 | 🟠 Major

Validate 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. Fold defaults.output_format into 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: Prefer slices.Equal over reflect.DeepEqual here.

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: emit has redundant dead conditional logic.

When s == nil || s.events == nil, both branches return nil; 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1d99df4 and 54c80b4.

⛔ Files ignored due to path filters (1)
  • README.md is excluded by !**/*.md
📒 Files selected for processing (26)
  • internal/cli/root.go
  • internal/cli/root_command_execution_test.go
  • internal/cli/root_test.go
  • internal/cli/testdata/exec_help.golden
  • internal/cli/workspace_config.go
  • internal/cli/workspace_config_test.go
  • internal/core/agent/client.go
  • internal/core/agent/client_test.go
  • internal/core/agent/registry.go
  • internal/core/agent/registry_test.go
  • internal/core/agent/session.go
  • internal/core/agent/session_helpers_test.go
  • internal/core/api.go
  • internal/core/api_test.go
  • internal/core/model/model.go
  • internal/core/run/command_io.go
  • internal/core/run/exec_flow.go
  • internal/core/run/exec_flow_integration_test.go
  • internal/core/run/execution.go
  • internal/core/run/execution_acp_integration_test.go
  • internal/core/run/execution_acp_test.go
  • internal/core/run/logging.go
  • internal/core/run/runtime_logger.go
  • internal/core/run/types.go
  • internal/core/workspace/config.go
  • internal/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

Comment thread internal/cli/root.go Outdated
Comment thread internal/core/agent/client.go
Comment thread internal/core/agent/registry.go
Comment thread internal/core/run/exec_flow_integration_test.go
Comment thread internal/core/run/exec_flow.go
Comment thread internal/core/run/exec_flow.go
Comment thread internal/core/run/execution_acp_integration_test.go Outdated
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