Skip to content

refactor: tui redesign - #201

Merged
pedronauck merged 8 commits into
mainfrom
wizard
Jun 15, 2026
Merged

pedronauck merged 8 commits into
mainfrom
wizard

Conversation

@pedronauck

@pedronauck pedronauck commented Jun 15, 2026 •

Copy link
Copy Markdown
Member

Summary by CodeRabbit

Release Notes

  • New Features

    • Added job control to pause a running job and resume it with a terminal-composer message (including remote/multi-run and review-watch views).
    • Added daemon-backed run-job pause/message endpoints and integrated them into the UI.
    • Introduced a full interactive task-run wizard for selecting workflows and configuring runtime/execution options.
    • Added workflow-scoped task runtime overrides.
  • Improvements

    • Enhanced terminal UI layout, spacing, and styling for clearer, consistent rendering.
    • Added contract-version metadata and compatibility handling during daemon bootstrap.

@pedronauck pedronauck self-assigned this Jun 15, 2026
@coderabbitai

coderabbitai Bot commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a7daac8-381c-4955-b8e5-9ace49a54721

📥 Commits

Reviewing files that changed from the base of the PR and between 9838340 and 9aa70d6.

⛔ Files ignored due to path filters (1)
  • .compozy/tasks/pr-201/reviews-003/issue_001.md is excluded by !**/*.md
📒 Files selected for processing (2)
  • internal/core/run/ui/update.go
  • internal/core/run/ui/update_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/run/ui/update_test.go
  • internal/core/run/ui/update.go

Walkthrough

This PR adds run-job pause and message control across daemon APIs, execution, storage, and terminal UIs. It also adds daemon contract-version reporting and compatibility checks, workflow-scoped task runtime overrides, a multi-step task-run wizard, and task-number propagation.

Changes

Run control and workflow execution stack

Layer / File(s) Summary
API contracts and transport wiring
internal/api/client/*, internal/api/contract/*, internal/api/core/*, internal/api/httpapi/*, internal/daemon/service.go, pkg/compozy/runs/integration_test.go
Adds run-job pause/message contracts, routes, handlers, client methods, daemon contract-version fields, and matching transport/OpenAPI tests.
Job control execution and persistence
internal/core/agent/*, internal/core/model/*, internal/core/run/internal/acpshared/*, internal/core/run/internal/runshared/*, internal/daemon/run_manager.go, internal/daemon/run_snapshot.go, internal/store/rundb/*, pkg/compozy/events/*
Introduces the job-control registry and session turn controller, adds cancel/prompt session support and message IDs, emits pause/resume events, updates daemon run control and snapshots, and persists user-message transcript rows and pause/resume job states.
CLI compatibility and task-run flow
internal/cli/*, internal/core/model/task_runtime.go, internal/core/plan/prepare.go, internal/core/tasks/parser.go, internal/core/workspace/config_validate.go, sdk/extension/types.go
Adds daemon contract compatibility checks, workflow-scoped task-runtime selectors and form state, multi-workflow interactive execution paths, task-number extraction and propagation, and validation for unsupported workspace-config workflow rules.
UI remote attach and rendering
internal/charmtheme/render.go, internal/core/run/ui/{header,layout,sidebar,styles,summary,timeline,validation_form,view}.go, internal/core/run/ui/*test.go, scripts/capture-sidebar-spacing.sh
Refactors terminal rendering toward foreground and border styling, updates headers, sidebars, timelines, dialogs, and help keycaps, and adds screenshot and layout regression coverage.

Sequence Diagram(s)

sequenceDiagram
  participant CLIUI
  participant APICoreHandlers
  participant RunManager
  participant JobControlRegistry
  participant SessionTurnController

  CLIUI->>APICoreHandlers: POST pause/messages
  APICoreHandlers->>RunManager: PauseRunJob / SendRunJobMessage
  RunManager->>JobControlRegistry: Pause / SendMessage
  JobControlRegistry->>SessionTurnController: control request
  SessionTurnController-->>CLIUI: status, session_id, message_id
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • compozy/compozy#109: Extends the same --task-runtime parsing and runtime-rule workflow that this PR expands with workflow=... support.
  • compozy/compozy#191: Touches the same CLI daemon compatibility and restart flow updated here with contract-version handling.
  • compozy/compozy#162: Introduces the multi-run UI structures that this PR extends with job-control wiring.
✨ 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 wizard

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

Caution

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

⚠️ Outside diff range comments (2)
internal/core/run/ui/multi_remote.go (2)

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

Restart the spinner loop after child bootstrap.

handleChildBootstrap can install a running child snapshot, but this branch returns nil, so a queue that had no spinner scheduled yet stays frozen until another child event or tab switch.

Proposed fix
 	case multiRunChildBootstrapMsg:
 		m.handleChildBootstrap(value)
-		return m, nil
+		return m, m.ensureSpinnerTick()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/ui/multi_remote.go` around lines 499 - 501, The case
statement for multiRunChildBootstrapMsg calls handleChildBootstrap but returns
nil, which prevents the spinner loop from restarting after a child snapshot is
installed. This leaves the queue frozen if no spinner was previously scheduled.
After calling handleChildBootstrap, ensure the spinner loop is restarted by
invoking the appropriate function to resume spinner queue processing instead of
directly returning m, nil.

815-817: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wire job-control callbacks for event-created child models.

When child events arrive before or without a bootstrap snapshot, the placeholder child never gets onJobControl, so pause/message shortcuts in that tab cannot call the daemon even though the callbacks exist on the multi-run model.

Proposed fix
 	if tab.child == nil {
 		tab.child = newPlaceholderChildModel(tab.slug, m.cfg, m.childWidth(), m.childHeight())
+		if tab.child != nil {
+			tab.child.onJobControl = newRemoteJobControlHandler(
+				firstNonEmpty(tab.runID, msg.RunID),
+				m.pauseRunJob,
+				m.sendRunJobMessage,
+			)
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/ui/multi_remote.go` around lines 815 - 817, The placeholder
child model created by newPlaceholderChildModel() in the tab.child
initialization block is missing job-control callbacks, which prevents
pause/message shortcuts from functioning. After creating the placeholder child
model (tab.child = newPlaceholderChildModel(...)), wire the job-control
callbacks from the multi-run model (m) to the newly created child model. This
ensures that when child events arrive before a bootstrap snapshot, the
placeholder still has the necessary callbacks to call the daemon for pause and
message operations.
🧹 Nitpick comments (3)
internal/core/run/ui/review_watch_remote_test.go (1)

56-68: ⚡ Quick win

Wrap this new case in t.Run("Should...") to satisfy test conventions.

This new test is currently a single top-level case; please wrap it as a named subtest to match the required test pattern.

As per coding guidelines, **/*_test.go must use t.Run("Should...") for all test cases and subtests as the default structure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/ui/review_watch_remote_test.go` around lines 56 - 68, The
test function TestReviewWatchModelAppliesWorkspaceRootToConfig needs to be
wrapped with a t.Run() subtest to follow the required test conventions. Move the
test body (after the t.Parallel() call) into a t.Run call with a descriptive
name following the "Should..." pattern to ensure compliance with the test
structure guidelines for *_test.go files.

Source: Coding guidelines

internal/core/model/job_control_test.go (1)

10-62: ⚡ Quick win

Refactor these tests into subtests (t.Run("Should...")), preferably table-driven.

Both test functions currently bundle scenarios inline; convert to named subtests so each behavior is isolated and aligned with the required pattern.

As per coding guidelines, **/*_test.go requires t.Run("Should...") for all test cases and table-driven subtests as the default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/model/job_control_test.go` around lines 10 - 62, Refactor both
test functions to use subtests with t.Run("Should...") naming pattern. For
TestJobControlRegistryRoutesByJobIDAndIndexAlias, break the current inline
scenarios (Pause operation, SendMessage operation with index alias, and
unregister behavior) into separate named subtests. For
TestValidateJobControlMessageRejectsEmptyAndOversizedMessages, convert the
validation checks (empty message, oversized message, and valid trimmed message)
into a table-driven approach with subtests for each test case. Each t.Run
subtest should have a clear descriptive name following the "Should..." pattern
that explains what behavior is being tested.

Source: Coding guidelines

internal/api/client/client_contract_test.go (1)

152-214: ⚡ Quick win

New tests in this PR are using top-level bodies instead of the required subtest pattern. The shared issue is test-structure drift from the repo standard requiring t.Run("Should...") (and table-driven subtests by default) for test cases.

  • internal/api/client/client_contract_test.go#L152-L214: wrap the pause/message scenario in t.Run("Should ...") (or table-drive it) and keep assertions inside the subtest.
  • internal/store/rundb/run_db_test.go#L244-L301: convert this single-case test into a t.Run("Should ...") subtest (or table-driven subtests for future variants).
  • internal/core/run/ui/sidebar_screenshot_test.go#L21-L45: wrap the screenshot assertion flow in a t.Run("Should ...") subtest to align with the required pattern.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/api/client/client_contract_test.go` around lines 152 - 214, The
tests across three files do not follow the required subtest pattern. In
internal/api/client/client_contract_test.go (lines 152-214), wrap the entire
body of the TestClientRunJobControlPostsPauseAndMessageRequests function inside
a t.Run("Should pause and message run jobs", ...) call, keeping all assertions
and logic inside the subtest closure. In internal/store/rundb/run_db_test.go
(lines 244-301), similarly wrap the test function body inside a t.Run("Should
...", ...) subtest with an appropriate descriptive name. In
internal/core/run/ui/sidebar_screenshot_test.go (lines 21-45), wrap the
screenshot assertion flow inside a t.Run("Should ...", ...) subtest to align
with the required test structure pattern. All three changes follow the same
pattern: move the test logic into a t.Run call while preserving the test
behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/api/core/handlers_test.go`:
- Around line 429-525: Refactor the test function
TestRunJobControlHandlersForwardPauseAndMessageRequests to use the t.Run subtest
pattern as required by project guidelines. Extract the pause endpoint testing
logic into a separate t.Run("Should pause run job", func(t *testing.T) {...})
subtest and the message endpoint testing logic into another t.Run("Should send
message to run job", func(t *testing.T) {...}) subtest. The shared setup
(handlers, engine, and middleware registration) can remain outside the subtests
before the individual t.Run calls. This will isolate failures to specific
functionality and align with the project's mandatory testing patterns.

In `@internal/cli/form_test.go`:
- Around line 495-556: The test functions
TestTaskRunFormInputsApplyMultipleWorkflowSelection and the other test case
mentioned in the consolidated sites are written as top-level test functions but
should follow the repository's testing convention by wrapping their test logic
in t.Run("Should...") subtests. Refactor each test function to use the t.Run
pattern where the subtest name describes the specific scenario being tested
(e.g., "Should apply multiple workflow selection correctly"), and move all the
test assertions and setup into the subtest body.

In `@internal/cli/root_command_execution_test.go`:
- Around line 1264-1342: The test function
TestTasksRunInteractiveFormCanStartMultipleWorkflows must be refactored to use
the required t.Run("Should...") pattern. Wrap all the existing test logic (from
the current function body) inside a t.Run call with a descriptive "Should..."
name that explains what the test validates, such as "Should start multiple
workflows from interactive form". The outer function should only contain the
t.Run call, and all other setup and assertions should be moved into the callback
function passed to t.Run.

In `@internal/cli/task_runtime_form.go`:
- Around line 53-75: The scopeWorkflow variable at line 53 is set to false for
single-workflow runs (when len(slugs) <= 1), which prevents proper lookup and
preseeding of workflow-qualified runtime rules at lines 133-143 and 170-174,
causing existing workflow-scoped overrides to be lost. Change the logic to
always use the actual workflow name for rule matching and key generation,
regardless of the number of workflows. Introduce a separate display-only
workflow variable (which can be empty for single-workflow mode) while keeping
the workflow parameter used in the populate call and rule matching logic
consistently scoped to the real workflow name. Update the form.populate call and
all rule lookup/matching logic at the cited lines to use the real workflow name
for keys and matching, with display labels using only the display variable.

In `@internal/cli/tasks_run_wizard_test.go`:
- Around line 15-369: Refactor the wizard tests to follow repository standards
by consolidating the multiple standalone top-level test functions
(TestTaskRunWizardModelSelectsMultipleWorkflowsAndSubmits,
TestTaskRunWizardModelPreservesAndReordersWorkflowSelection,
TestTaskRunWizardModelBuildsWorkflowScopedOverrides,
TestTaskRunWizardModelPreservesOverridesAcrossBackNavigation,
TestTaskRunWizardTextInputAcceptsGlobalShortcutCharacters,
TestTaskRunWizardRuntimeTextInputAcceptsNavigationLetters,
TestTaskRunWizardOverrideTextInputAcceptsNavigationLetters,
TestTaskRunWizardModelFiltersWorkflowSelection,
TestTaskRunWizardModelAcceptsManualWorkflowFallback, and
TestTaskRunWizardViewFitsTerminalBounds) into fewer parent test functions that
use t.Run("Should...") subtests for each test case. Implement table-driven test
patterns where applicable, such as for TestTaskRunWizardViewFitsTerminalBounds
which already has multiple dimensions to test. Each subtest should follow the
pattern t.Run("Should<describe_behavior>", func(t *testing.T) { ... }) and tests
should call t.Parallel() within each subtest as appropriate.

In `@internal/core/model/job_control.go`:
- Around line 157-168: In the completeJobControlResponse function, the RunID and
JobID fields are conditionally assigned only when empty, which allows mismatched
values to propagate. Remove the conditional checks for empty values and
unconditionally assign resp.RunID = req.RunID and resp.JobID = req.JobID to
ensure the response always contains the normalized request identities, enforcing
the API contract regardless of what the controller initially returned.

In `@internal/core/model/task_runtime.go`:
- Around line 81-83: The condition in the TaskRuntime matching logic (line
81-83) incorrectly treats whitespace-only workflow qualifiers as set to empty
string instead of unset. Currently, it returns false whenever Workflow is not
nil, even if it trims to empty string. Fix this by adding an additional check:
only enforce the workflow equality comparison if the trimmed Workflow value is
non-empty. Modify the condition to check that after trimming, the Workflow is
not empty before comparing it with the target's workflow. This ensures
blank/whitespace-only qualifiers are treated as unscoped rules that match any
workflow, rather than rules that match only empty workflows.

In `@internal/core/plan/prepare_test.go`:
- Around line 288-290: The comparison using wantTaskNumbers[job.CodeFiles[0]]
relies on implicit zero-value semantics for missing keys, which can mask bugs
when job.TaskNumber is also 0. Instead, use the two-value form of map lookup to
explicitly verify that the key exists in the wantTaskNumbers map before
asserting the task number. Check that job.CodeFiles[0] is present as a key in
wantTaskNumbers, and only compare values when the key exists, otherwise fail the
test with a clear message indicating the missing key.

In `@internal/core/run/internal/acpshared/session_exec.go`:
- Around line 315-317: The Pause() and SendMessage() methods in session_exec.go
should not silently fall back to context.Background() when receiving a nil
context, as this breaks cancellation and deadline propagation across runtime
boundaries. Replace the conditional check that assigns context.Background() to
ctx with code that returns an explicit error when ctx is nil. This ensures
callers are notified of invalid input rather than having their cancellation
signals ignored when these methods call external services like CancelSession().

In `@internal/core/run/transcript/model_test.go`:
- Around line 74-109: The test function
TestSessionViewModelAppendsUserMessagesAsDistinctEntries is structured as a
top-level test body instead of following the required subtest wrapper pattern.
Wrap the entire test logic inside a t.Run() call using a descriptive "Should..."
naming convention (for example, "Should append user messages as distinct
entries"), and move the test body into the callback function passed to t.Run().
This ensures consistency with the project's coding guidelines for all test cases
in _test.go files.

In `@internal/core/run/transcript/model.go`:
- Around line 345-356: The findEntryID method searches for entries by ID without
restricting to user-message entries only. This allows user message IDs to
collide with non-user entry IDs, causing applyUserMessage to incorrectly merge
user content into wrong entries. Modify the findEntryID method to add an
additional condition in the entry lookup loop that checks whether each entry is
a user-message type before matching on ID, ensuring only user-message entries
are returned from the search.

In `@internal/core/run/ui/model_test.go`:
- Around line 93-103: The test loop iterating over jobState values (jobPending,
jobRunning, jobRetrying, jobSuccess, jobFailed) needs to be refactored to use
the t.Run pattern with named subtests. For each state in the loop, wrap the
existing assertions for jobStateIcon, jobStateColor, and jobBorderColor inside a
t.Run call with a descriptive name following the "Should..." convention (for
example, naming the subtest based on the current state being tested). This
isolates each state's test results and complies with the repository's test
policy requiring t.Run subtests for all test cases.

In `@internal/core/run/ui/update_test.go`:
- Around line 136-238: The test functions
TestPauseSelectedJobEnablesComposerAndMessageResumes (lines 136-238), and the
other affected test functions at lines 341-372, 461-491, and 870-932 in file
internal/core/run/ui/update_test.go do not follow the required
t.Run("Should...") subtest pattern. For each of these test functions, wrap the
main test logic inside a t.Run call with a descriptive subtest name that
explains what the test should do (following the pattern t.Run("Should...",
func(t *testing.T) { ... })), rather than having the test body directly in the
top-level function. This ensures consistency with the coding guidelines that
require t.Run subtests for all test cases.

In `@internal/core/run/ui/update.go`:
- Around line 224-225: The code creates timeout contexts using
context.Background() which detaches them from the UI session's lifecycle,
allowing operations to continue for the full 30-second timeout even when the UI
shuts down. Add a context field to the uiModel struct, pass the cancellation
context from uiController to uiModel during initialization, and then replace
context.Background() with this passed context as the base for the timeout
contexts in the context.WithTimeout calls at lines 224-225 and 251-252. This
ensures job-control requests respect the UI/session lifecycle.

In `@internal/core/run/ui/view_test.go`:
- Around line 1487-1497: The assertNoForcedBackground function only checks for
truecolor (48;2;) and 256-color (48;5;) background SGR codes, but misses
16-color background codes (40–47 for standard colors and 100–107 for bright
colors) and reverse video (7). Extend the conditional check to include these
additional SGR patterns alongside the existing checks for 48;2; and 48;5;,
ensuring the function detects all possible ways a forced background could be
applied to the content string.
- Around line 38-60: The test function
TestJobsBodyFitsWindowWidthAcrossBreakpoints and the other test functions
referenced in the consolidated sites (at lines 358-494, 584-742, 1077-1157,
1425-1480, and 1544-1707) are not following the required "Should..." naming
pattern for subtests. Rename all t.Run calls across these test functions to use
descriptive "Should..." names instead of format-based names like "80x24". For
example, replace the format-string-based subtest name with a descriptive name
like "Should fit jobs body width at breakpoint" or similar that clearly explains
what the test case validates. This should be applied consistently across all the
mentioned test functions in the file to comply with the table-driven test
pattern with descriptive subtest naming requirement.

In `@internal/daemon/run_snapshot.go`:
- Around line 175-191: The jobQueuedTaskNumber function returns 0 prematurely
when both TaskTitle and TaskType are empty, preventing it from attempting to
extract task identity numbers from CodeFile and CodeFiles. Remove the early
return gate that checks if both TaskTitle and TaskType are empty strings (the
condition that returns 0 before the ExtractTaskIdentityNumber calls). This
allows the function to proceed and extract valid task numbers from CodeFile and
CodeFiles even when the metadata fields are absent.

In `@internal/store/rundb/run_db.go`:
- Around line 1591-1613: The three functions projectJobPausingState,
projectJobPausedState, and projectJobResumedState all call
jobIDFromIndex(payload.Index, "") with an empty string as the second argument,
which results in an empty job_id when Index equals 0, breaking state continuity.
Fix each function by providing a non-empty fallback value to the second
parameter of jobIDFromIndex instead of an empty string, ensuring these
pause/resume events are correctly keyed under the proper job ID regardless of
the Index value. Apply the same fix to any other similar functions in the
indicated line range that have the same pattern.

---

Outside diff comments:
In `@internal/core/run/ui/multi_remote.go`:
- Around line 499-501: The case statement for multiRunChildBootstrapMsg calls
handleChildBootstrap but returns nil, which prevents the spinner loop from
restarting after a child snapshot is installed. This leaves the queue frozen if
no spinner was previously scheduled. After calling handleChildBootstrap, ensure
the spinner loop is restarted by invoking the appropriate function to resume
spinner queue processing instead of directly returning m, nil.
- Around line 815-817: The placeholder child model created by
newPlaceholderChildModel() in the tab.child initialization block is missing
job-control callbacks, which prevents pause/message shortcuts from functioning.
After creating the placeholder child model (tab.child =
newPlaceholderChildModel(...)), wire the job-control callbacks from the
multi-run model (m) to the newly created child model. This ensures that when
child events arrive before a bootstrap snapshot, the placeholder still has the
necessary callbacks to call the daemon for pause and message operations.

---

Nitpick comments:
In `@internal/api/client/client_contract_test.go`:
- Around line 152-214: The tests across three files do not follow the required
subtest pattern. In internal/api/client/client_contract_test.go (lines 152-214),
wrap the entire body of the TestClientRunJobControlPostsPauseAndMessageRequests
function inside a t.Run("Should pause and message run jobs", ...) call, keeping
all assertions and logic inside the subtest closure. In
internal/store/rundb/run_db_test.go (lines 244-301), similarly wrap the test
function body inside a t.Run("Should ...", ...) subtest with an appropriate
descriptive name. In internal/core/run/ui/sidebar_screenshot_test.go (lines
21-45), wrap the screenshot assertion flow inside a t.Run("Should ...", ...)
subtest to align with the required test structure pattern. All three changes
follow the same pattern: move the test logic into a t.Run call while preserving
the test behavior.

In `@internal/core/model/job_control_test.go`:
- Around line 10-62: Refactor both test functions to use subtests with
t.Run("Should...") naming pattern. For
TestJobControlRegistryRoutesByJobIDAndIndexAlias, break the current inline
scenarios (Pause operation, SendMessage operation with index alias, and
unregister behavior) into separate named subtests. For
TestValidateJobControlMessageRejectsEmptyAndOversizedMessages, convert the
validation checks (empty message, oversized message, and valid trimmed message)
into a table-driven approach with subtests for each test case. Each t.Run
subtest should have a clear descriptive name following the "Should..." pattern
that explains what behavior is being tested.

In `@internal/core/run/ui/review_watch_remote_test.go`:
- Around line 56-68: The test function
TestReviewWatchModelAppliesWorkspaceRootToConfig needs to be wrapped with a
t.Run() subtest to follow the required test conventions. Move the test body
(after the t.Parallel() call) into a t.Run call with a descriptive name
following the "Should..." pattern to ensure compliance with the test structure
guidelines for *_test.go files.
🪄 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: 3c59c7d8-6082-4d16-bfd1-4f7ccee06272

📥 Commits

Reviewing files that changed from the base of the PR and between 8e99a28 and 989af45.

⛔ Files ignored due to path filters (12)
  • .agents/skills/grill-me/SKILL.md is excluded by !**/*.md, !.agents/**
  • .agents/skills/handoff/SKILL.md is excluded by !**/*.md, !.agents/**
  • .claude/ledger/2026-06-14-MEMORY-exec-tui-polish.md is excluded by !**/*.md, !.claude/**
  • .claude/ledger/2026-06-14-MEMORY-exec-tui-redesign.md is excluded by !**/*.md, !.claude/**
  • .codex/ledger/2026-06-14-MEMORY-task-number.md is excluded by !**/*.md
  • .codex/ledger/2026-06-14-MEMORY-task-run-multiple-tui.md is excluded by !**/*.md
  • .codex/ledger/2026-06-15-MEMORY-task-pause-message.md is excluded by !**/*.md
  • .codex/ledger/2026-06-15-MEMORY-tui-token-layout.md is excluded by !**/*.md
  • docs/events.md is excluded by !**/*.md
  • openapi/compozy-daemon.json is excluded by !**/*.json
  • skills-lock.json is excluded by !**/*.json
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**
📒 Files selected for processing (103)
  • internal/api/client/client.go
  • internal/api/client/client_contract_test.go
  • internal/api/client/runs.go
  • internal/api/contract/contract_test.go
  • internal/api/contract/routes.go
  • internal/api/contract/types.go
  • internal/api/core/errors.go
  • internal/api/core/handlers.go
  • internal/api/core/handlers_service_errors_test.go
  • internal/api/core/handlers_smoke_test.go
  • internal/api/core/handlers_test.go
  • internal/api/core/interfaces.go
  • internal/api/core/routes.go
  • internal/api/httpapi/openapi_contract_test.go
  • internal/api/httpapi/transport_integration_test.go
  • internal/charmtheme/render.go
  • internal/cli/agents_commands.go
  • internal/cli/agents_commands_test.go
  • internal/cli/daemon_commands.go
  • internal/cli/daemon_commands_test.go
  • internal/cli/daemon_exec_test_helpers_test.go
  • internal/cli/form.go
  • internal/cli/form_test.go
  • internal/cli/reviews_exec_daemon.go
  • internal/cli/root_command_execution_test.go
  • internal/cli/run_observe.go
  • internal/cli/task_runtime_flag.go
  • internal/cli/task_runtime_flag_test.go
  • internal/cli/task_runtime_form.go
  • internal/cli/tasks_run_wizard.go
  • internal/cli/tasks_run_wizard_test.go
  • internal/cli/tasks_run_wizard_view.go
  • internal/core/agent/acp_convert.go
  • internal/core/agent/acp_convert_test.go
  • internal/core/agent/client.go
  • internal/core/contentconv/contentconv.go
  • internal/core/model/content.go
  • internal/core/model/job_control.go
  • internal/core/model/job_control_test.go
  • internal/core/model/model_test.go
  • internal/core/model/preparation.go
  • internal/core/model/runtime_config.go
  • internal/core/model/task_runtime.go
  • internal/core/plan/prepare.go
  • internal/core/plan/prepare_test.go
  • internal/core/run/exec/exec_integration_test.go
  • internal/core/run/exec/run_agent_engine_integration_test.go
  • internal/core/run/executor/aliases.go
  • internal/core/run/executor/event_stream.go
  • internal/core/run/executor/execution.go
  • internal/core/run/executor/execution_acp_test.go
  • internal/core/run/executor/execution_ui_test.go
  • internal/core/run/executor/hooks.go
  • internal/core/run/internal/acpshared/command_io.go
  • internal/core/run/internal/acpshared/command_io_test.go
  • internal/core/run/internal/acpshared/session_exec.go
  • internal/core/run/internal/acpshared/session_exec_test.go
  • internal/core/run/internal/runshared/config.go
  • internal/core/run/internal/runshared/shutdown.go
  • internal/core/run/transcript/model.go
  • internal/core/run/transcript/model_test.go
  • internal/core/run/ui/adapter_test.go
  • internal/core/run/ui/aliases.go
  • internal/core/run/ui/header.go
  • internal/core/run/ui/layout.go
  • internal/core/run/ui/model.go
  • internal/core/run/ui/model_test.go
  • internal/core/run/ui/multi_remote.go
  • internal/core/run/ui/multi_remote_test.go
  • internal/core/run/ui/remote.go
  • internal/core/run/ui/remote_test.go
  • internal/core/run/ui/review_watch_remote.go
  • internal/core/run/ui/review_watch_remote_test.go
  • internal/core/run/ui/sidebar.go
  • internal/core/run/ui/sidebar_screenshot_test.go
  • internal/core/run/ui/styles.go
  • internal/core/run/ui/summary.go
  • internal/core/run/ui/timeline.go
  • internal/core/run/ui/types.go
  • internal/core/run/ui/update.go
  • internal/core/run/ui/update_test.go
  • internal/core/run/ui/validation_form.go
  • internal/core/run/ui/validation_form_test.go
  • internal/core/run/ui/view.go
  • internal/core/run/ui/view_test.go
  • internal/core/tasks/parser.go
  • internal/core/tasks/parser_test.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_validate.go
  • internal/daemon/run_manager.go
  • internal/daemon/run_snapshot.go
  • internal/daemon/run_snapshot_test.go
  • internal/daemon/service.go
  • internal/daemon/service_test.go
  • internal/store/rundb/run_db.go
  • internal/store/rundb/run_db_test.go
  • pkg/compozy/events/docs_test.go
  • pkg/compozy/events/event.go
  • pkg/compozy/events/kinds/job.go
  • pkg/compozy/events/kinds/session.go
  • pkg/compozy/runs/integration_test.go
  • scripts/capture-sidebar-spacing.sh
  • sdk/extension/types.go

Comment thread internal/api/core/handlers_test.go
Comment thread internal/cli/form_test.go
Comment thread internal/cli/root_command_execution_test.go
Comment thread internal/cli/task_runtime_form.go Outdated
Comment thread internal/cli/tasks_run_wizard_test.go Outdated
Comment thread internal/core/run/ui/update.go Outdated
Comment thread internal/core/run/ui/view_test.go
Comment thread internal/core/run/ui/view_test.go Outdated
Comment thread internal/daemon/run_snapshot.go
Comment thread internal/store/rundb/run_db.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: 1

Caution

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

⚠️ Outside diff range comments (2)
internal/core/run/ui/view.go (1)

126-136: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle canceled/crashed terminal states explicitly in runChipStatus.

Line 133 can currently render DONE for non-success terminal outcomes (for example canceled/crashed paths with failed == 0). This misreports final run state in the workflow chip.

💡 Suggested fix
 func (m *uiModel) runChipStatus() (string, string, color.Color) {
 	switch {
 	case m.shutdown.Active():
 		return glyphActiveDot, "STOPPING", colorWarning
+	case m.parentRun.Status == remoteRunStatusCanceled:
+		return glyphActiveDot, "CANCELED", colorWarning
+	case m.parentRun.Status == remoteRunStatusFailed || m.parentRun.Status == remoteRunStatusCrashed:
+		return jobIconFailed, "FAILED", colorError
 	case m.isRunComplete() && m.failed > 0:
 		return jobIconFailed, "FAILED", colorError
 	case m.isRunComplete():
 		return jobIconSuccess, "DONE", colorSuccess
 	default:
 		return glyphActiveDot, "RUNNING", colorAccentAlt
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/ui/view.go` around lines 126 - 136, The runChipStatus
method in the switch statement has a logic issue where the case
m.isRunComplete() condition (line 133) can render DONE status for non-success
terminal outcomes like canceled or crashed runs when failed equals zero,
misreporting the actual final run state. Add explicit case conditions to check
for canceled and crashed terminal states before the generic m.isRunComplete()
case, ensuring these states return appropriate status indicators and icons
rather than falling through to the DONE status.
internal/daemon/run_snapshot_test.go (1)

257-342: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align new tests with the required t.Run("Should...") structure.

The new test coverage here still mixes non-compliant naming (t.Run(tc.name, ...) with values like "pausing") and a standalone top-level case without a t.Run("Should...") wrapper. Please wrap each test case in Should... subtests and update table case names accordingly.

As per coding guidelines, **/*_test.go must use t.Run("Should...") for all test cases and use table-driven tests with subtests as the default pattern.

Also applies to: 345-372

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/daemon/run_snapshot_test.go` around lines 257 - 342, The test
function TestRunSnapshotBuilderTracksJobPauseAndResumeStates uses non-compliant
naming in its t.Run calls with generic names like "pausing", "paused", and
"resumed" instead of descriptive "Should..." names. Update the t.Run calls to
use descriptive "Should..." prefixed names that clearly describe the expected
behavior (for example, "Should track job pausing state", "Should track job
paused state", "Should track job resumed state"). Apply this change to all
subtest invocations in the test table cases shown in the diff, and also ensure
the same "Should..." pattern is consistently applied to any related test cases
in the line range 345-372 that the comment references.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/core/run/ui/view_test.go`:
- Around line 1604-1614: The sgrParamsHaveForcedBackground function incorrectly
flags extended-color foreground sequences as having a background because it
checks for isolated tokens matching background values without considering
context. The issue is that in a sequence like "38;5;48", the value 48 is a color
index for foreground (indicated by the leading 38), not a background color
selector. Fix the function by tracking when you encounter a 38 (foreground) or
48 (background) token followed by 5 or 2, then properly skip over or interpret
the subsequent color parameter tokens based on that context. Return true only
when you encounter 48 with a mode (5 or 2), or basic background color codes
(40-47, 100-107) that stand alone. Additionally, add a test case in
TestHasForcedBackgroundSGR that verifies the string "\x1b[38;5;48mtext\x1b[0m"
returns false.

---

Outside diff comments:
In `@internal/core/run/ui/view.go`:
- Around line 126-136: The runChipStatus method in the switch statement has a
logic issue where the case m.isRunComplete() condition (line 133) can render
DONE status for non-success terminal outcomes like canceled or crashed runs when
failed equals zero, misreporting the actual final run state. Add explicit case
conditions to check for canceled and crashed terminal states before the generic
m.isRunComplete() case, ensuring these states return appropriate status
indicators and icons rather than falling through to the DONE status.

In `@internal/daemon/run_snapshot_test.go`:
- Around line 257-342: The test function
TestRunSnapshotBuilderTracksJobPauseAndResumeStates uses non-compliant naming in
its t.Run calls with generic names like "pausing", "paused", and "resumed"
instead of descriptive "Should..." names. Update the t.Run calls to use
descriptive "Should..." prefixed names that clearly describe the expected
behavior (for example, "Should track job pausing state", "Should track job
paused state", "Should track job resumed state"). Apply this change to all
subtest invocations in the test table cases shown in the diff, and also ensure
the same "Should..." pattern is consistently applied to any related test cases
in the line range 345-372 that the comment references.
🪄 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: 0d4e32fe-ad08-478e-9acf-37ce479dbde2

📥 Commits

Reviewing files that changed from the base of the PR and between 989af45 and caeadaa.

⛔ Files ignored due to path filters (22)
  • .compozy/tasks/pr-201/reviews-001/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_003.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_004.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_005.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_006.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_007.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_008.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_009.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_010.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_011.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_012.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_013.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_014.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_015.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_016.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_017.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_018.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_019.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_020.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_021.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-001/issue_022.md is excluded by !**/*.md
📒 Files selected for processing (27)
  • internal/api/core/handlers_test.go
  • internal/cli/form_test.go
  • internal/cli/root_command_execution_test.go
  • internal/cli/task_runtime_form.go
  • internal/cli/tasks_run_wizard_test.go
  • internal/core/model/job_control.go
  • internal/core/model/job_control_test.go
  • internal/core/model/model_test.go
  • internal/core/model/task_runtime.go
  • internal/core/plan/prepare_test.go
  • internal/core/run/internal/acpshared/session_exec.go
  • internal/core/run/internal/acpshared/session_exec_test.go
  • internal/core/run/transcript/model.go
  • internal/core/run/transcript/model_test.go
  • internal/core/run/ui/model.go
  • internal/core/run/ui/model_test.go
  • internal/core/run/ui/multi_remote.go
  • internal/core/run/ui/multi_remote_test.go
  • internal/core/run/ui/review_watch_remote_test.go
  • internal/core/run/ui/update.go
  • internal/core/run/ui/update_test.go
  • internal/core/run/ui/view.go
  • internal/core/run/ui/view_test.go
  • internal/daemon/run_snapshot.go
  • internal/daemon/run_snapshot_test.go
  • internal/store/rundb/run_db.go
  • internal/store/rundb/run_db_test.go
💤 Files with no reviewable changes (1)
  • internal/daemon/run_snapshot.go
🚧 Files skipped from review as they are similar to previous changes (21)
  • internal/core/run/ui/review_watch_remote_test.go
  • internal/core/run/transcript/model_test.go
  • internal/core/model/model_test.go
  • internal/core/run/ui/model_test.go
  • internal/api/core/handlers_test.go
  • internal/core/run/transcript/model.go
  • internal/core/plan/prepare_test.go
  • internal/core/model/job_control_test.go
  • internal/core/model/job_control.go
  • internal/store/rundb/run_db_test.go
  • internal/core/run/internal/acpshared/session_exec_test.go
  • internal/core/run/internal/acpshared/session_exec.go
  • internal/cli/root_command_execution_test.go
  • internal/core/run/ui/update.go
  • internal/cli/form_test.go
  • internal/core/run/ui/multi_remote.go
  • internal/store/rundb/run_db.go
  • internal/core/run/ui/update_test.go
  • internal/core/run/ui/model.go
  • internal/cli/task_runtime_form.go
  • internal/core/model/task_runtime.go

Comment thread internal/core/run/ui/view_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.

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/ui/update.go (1)

60-88: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Route runStatusMsg through single-message dispatch or it gets dropped.

applyUIMsg now supports runStatusMsg, but dispatchSingleUIMsg does not. Since remote bootstrap enqueues messages individually (session.Enqueue(msg)), runStatusMsg can be ignored in the default Update path, so m.runStatus never updates from those events.

Suggested fix
func (m *uiModel) dispatchSingleUIMsg(msg tea.Msg) (tea.Cmd, bool) {
	switch v := msg.(type) {
	case jobQueuedMsg:
		return m.applyUIMsg(v), true
	case jobStartedMsg:
		return m.applyUIMsg(v), true
	case jobRetryMsg:
		return m.applyUIMsg(v), true
	case jobPausingMsg:
		return m.applyUIMsg(v), true
	case jobPausedMsg:
		return m.applyUIMsg(v), true
	case jobResumedMsg:
		return m.applyUIMsg(v), true
	case jobFinishedMsg:
		return m.applyUIMsg(v), true
	case jobUpdateMsg:
		return m.applyUIMsg(v), true
	case usageUpdateMsg:
		return m.applyUIMsg(v), true
+	case runStatusMsg:
+		return m.applyUIMsg(v), true
	case shutdownStatusMsg:
		return m.applyUIMsg(v), true
	case jobFailureMsg:
		return m.applyUIMsg(v), true
	case jobControlResultMsg:
		return m.applyUIMsg(v), true
	default:
		return nil, false
	}
}

Also applies to: 127-129

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/ui/update.go` around lines 60 - 88, The dispatchSingleUIMsg
function does not include a case for runStatusMsg, so these messages fall
through to the default case and are dropped instead of being processed by
applyUIMsg. Add a case statement for runStatusMsg in the switch statement within
dispatchSingleUIMsg that calls m.applyUIMsg(v) and returns true, following the
same pattern as the other message type cases like jobQueuedMsg, jobStartedMsg,
etc.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/core/run/ui/update.go`:
- Around line 60-88: The dispatchSingleUIMsg function does not include a case
for runStatusMsg, so these messages fall through to the default case and are
dropped instead of being processed by applyUIMsg. Add a case statement for
runStatusMsg in the switch statement within dispatchSingleUIMsg that calls
m.applyUIMsg(v) and returns true, following the same pattern as the other
message type cases like jobQueuedMsg, jobStartedMsg, etc.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d6752000-30d8-47ee-8601-8a2d52c77202

📥 Commits

Reviewing files that changed from the base of the PR and between caeadaa and 9838340.

⛔ Files ignored due to path filters (3)
  • .compozy/tasks/pr-201/reviews-002/issue_001.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-002/issue_002.md is excluded by !**/*.md
  • .compozy/tasks/pr-201/reviews-002/issue_003.md is excluded by !**/*.md
📒 Files selected for processing (9)
  • internal/core/run/ui/model.go
  • internal/core/run/ui/remote.go
  • internal/core/run/ui/remote_test.go
  • internal/core/run/ui/timeline.go
  • internal/core/run/ui/types.go
  • internal/core/run/ui/update.go
  • internal/core/run/ui/view.go
  • internal/core/run/ui/view_test.go
  • internal/daemon/run_snapshot_test.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/core/run/ui/remote.go
  • internal/daemon/run_snapshot_test.go
  • internal/core/run/ui/view.go
  • internal/core/run/ui/timeline.go
  • internal/core/run/ui/model.go
  • internal/core/run/ui/types.go
  • internal/core/run/ui/view_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 15, 2026
@pedronauck
pedronauck merged commit 62d9792 into main Jun 15, 2026
7 checks passed
@pedronauck
pedronauck deleted the wizard branch June 15, 2026 22:18
MatheusBBarni added a commit to MatheusBBarni/compozy that referenced this pull request Jun 16, 2026
Resolve two conflicts introduced by the upstream TUI redesign (compozy#201):
- internal/cli/daemon_commands.go: keep version.UnstampedCommit (same "none"
  value as upstream's noneValue, but semantically precise for a build commit).
- internal/core/run/ui/multi_remote.go: port the multi-run worktree handoff
  line onto the redesigned tab-strip API (renderBrandTabsRow, charmtheme.Keycap,
  bg-less renderGap/renderOwnedLineKnownOwned). multiRunTabsHeight stays 3 to
  reserve the tab line, worktree status line, and separator.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
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