Skip to content

feat: add provider-neutral ACP speed control - #267

Merged
pedronauck merged 21 commits into
compozy:mainfrom
MatheusBBarni:feat/acp-speed-control
Jul 28, 2026
Merged

pedronauck merged 21 commits into
compozy:mainfrom
MatheusBBarni:feat/acp-speed-control

Conversation

@MatheusBBarni

@MatheusBBarni MatheusBBarni commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a canonical normal or fast speed preference across CLI, config, daemon, SDK, and execution paths
  • apply supported ACP speed options atomically before prompt dispatch with explicit applied, unsupported, or rejected outcomes
  • persist speed intent and per-job resolutions through events, snapshots, public run readers, results, and TUI surfaces
  • validate non-CLI speed inputs and hydrate list and workspace-watch summaries consistently

Verification

  • make verify
  • 4,699 Go tests passed with 4 skipped
  • frontend lint, typecheck, tests, and build passed
  • 5 Playwright E2E tests passed

Summary by CodeRabbit

  • New Features

    • Added normal and fast speed options for executions, tasks, reviews, and workspace configuration.
    • Speed preferences now carry through nested, parallel, resumed, and recovery runs.
    • Run summaries, events, APIs, and the sidebar now show requested speed and resolution status.
    • Added clear outcomes for applied, unsupported, and rejected speed requests.
  • Bug Fixes

    • Preserved speed settings across retries, resumes, snapshots, and historical data without inventing missing values.
    • Added validation and conflict detection for invalid or incompatible speed settings.
  • Documentation

    • Updated CLI help and speed-related configuration, event, and skill documentation.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

@MatheusBBarni is attempting to deploy a commit to the Compozy Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds canonical normal/fast speed configuration and provider resolution across CLI, ACP sessions, execution persistence, lifecycle events, APIs, run summaries, snapshots, and UI rendering. Historical payloads remain compatible without inventing speed data.

Changes

Speed configuration and contracts

Layer / File(s) Summary
Canonical speed and configuration plumbing
pkg/compozy/events/kinds/*, internal/core/model/*, internal/cli/*, internal/core/workspace/*
Adds speed types, validation, CLI flags, workspace precedence, explicit-runtime tracking, and persisted-run compatibility checks.
ACP session setup
internal/core/agent/*, internal/core/run/internal/acpshared/*
Session creation and resumption accept speed requests, resolve provider configuration atomically, return SessionStart, and report resolution outcomes.
Execution and event persistence
internal/core/run/exec/*, internal/core/run/executor/*, internal/daemon/*
Persists requested speed and per-attempt resolutions, emits lifecycle fields, records rejected setup outcomes, and prevents retries after rejected speed setup.
Run summaries and UI projection
pkg/compozy/runs/*, internal/core/run/ui/*, internal/api/*
Projects speed through snapshots, summaries, remote watches, API contracts, UI messages, sidebar labels, and generated OpenAPI assertions while preserving historical absence.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant RunManager
  participant ACPClient
  participant Executor
  participant RunSnapshot
  participant UI
  CLI->>RunManager: submit speed runtime override
  RunManager->>ACPClient: create or resume session with speed
  ACPClient->>ACPClient: resolve provider speed configuration
  ACPClient-->>Executor: SessionStart with SpeedResolution
  Executor-->>RunManager: emit speed lifecycle events
  RunManager->>RunSnapshot: apply speed and resolution by attempt
  RunSnapshot-->>UI: project requested speed and resolution
Loading

Possibly related PRs

  • compozy/compozy#34 — Establishes the ACP client/session abstractions extended by this session-start contract.
  • compozy/compozy#78 — Modifies the same session request contracts used here for speed forwarding.
  • compozy/compozy#198 — Removes legacy model setup behavior that this speed-resolution flow replaces.

Suggested reviewers: pedronauck

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding provider-neutral ACP speed control.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/acp-speed-control

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

❤️ Share

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef44e8c81c

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread sdk/extension/types.go Outdated
Comment thread pkg/compozy/runs/summary.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: 19

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/internal/acpshared/command_io.go (1)

347-364: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Emitting &speedResolution unconditionally publishes an empty resolution instead of omitting it.

SpeedResolution is a *SpeedResolution with omitempty, but a pointer to a zero-value struct is never omitted. When no speed was requested/resolved (zero kinds.SpeedResolution{}), session.started now carries "speed_resolution":{"requested":"","status":""}. Downstream consumers gate purely on nil — internal/core/run/ui/model.go Lines 1211-1217 would emit a jobSpeedMsg with an empty resolution, and internal/daemon/run_snapshot.go Line 356 would store an empty summary resolution — which conflicts with the "do not invent speed data" contract exercised by internal/core/run/ui/speed_test.go.

🐛 Proposed fix
 	event, err := runtimeevents.NewRuntimeEvent(
 		runID,
 		events.EventKindSessionStarted,
 		kinds.SessionStartedPayload{
 			Index:           index,
 			ACPSessionID:    identity.ACPSessionID,
 			AgentSessionID:  identity.AgentSessionID,
 			Resumed:         identity.Resumed,
-			SpeedResolution: &speedResolution,
+			SpeedResolution: speedResolutionPointer(speedResolution),
 		},
 	)
func speedResolutionPointer(resolution kinds.SpeedResolution) *kinds.SpeedResolution {
	if resolution == (kinds.SpeedResolution{}) {
		return nil
	}
	return &resolution
}
🤖 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/internal/acpshared/command_io.go` around lines 347 - 364,
Update the session-started runtime event construction in the surrounding command
function to pass nil for a zero-value kinds.SpeedResolution and a pointer only
when it contains resolved data. Reuse or add a small speedResolutionPointer
helper for this conversion, then assign its result to SpeedResolution so
downstream consumers continue using nil to detect absent speed information.
🧹 Nitpick comments (21)
internal/api/httpapi/openapi_contract_test.go (2)

541-558: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use slices.Sort for consistency with the new slices import.

♻️ Proposed tweak
-	sort.Strings(values)
+	slices.Sort(values)

Verify sort is still needed elsewhere in the file before dropping the import.

🤖 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/httpapi/openapi_contract_test.go` around lines 541 - 558, Update
schemaEnumStrings to sort values with slices.Sort instead of sort.Strings,
reusing the file’s existing slices import; then remove the sort import if no
other references remain in the file.

350-373: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exact-string matching against generated TypeScript is brittle.

These fragments depend on the generator's exact spacing, member ordering, and union ordering; a harmless regeneration (or generator upgrade) breaks the suite even though the contract is unchanged. The spec-level test above already pins the canonical literals — consider asserting only that each Speed* type is a union of the expected members (e.g. regex per type, order-insensitive) and that the fields are optional/required. Also wrap this in a Should … subtest per the test conventions.

🤖 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/httpapi/openapi_contract_test.go` around lines 350 - 373, Update
TestGeneratedOpenAPIContractKeepsSpeedFieldsOptionalAndLiteral to avoid exact
generated TypeScript fragment matching: use a Should subtest and assertions that
each Speed* type contains the expected union members order-independently, while
separately verifying speed and speed_resolution are optional and
requested/status are required. Preserve the existing canonical literals and
remove dependence on generator spacing, ordering, or formatting.

Source: Path instructions

internal/core/workspace/config_test.go (2)

1841-1844: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Asserting the exact TOML decoder message couples the suite to the library's wording.

"decode workspace config: strict mode: fields in the document are missing in the target struct" will break on any go-toml message change. Asserting the wrapper prefix plus a strings.Contains on "strict mode" keeps the intent without the coupling.

🤖 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/workspace/config_test.go` around lines 1841 - 1844, Update the
error assertion in the strict-mode workspace configuration test to verify the
stable “decode workspace config:” wrapper prefix and use strings.Contains to
check for “strict mode” rather than comparing the TOML decoder’s full message.
Preserve the existing failure reporting and ensure the test imports strings if
needed.

1661-1699: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New tests across the speed cohort skip the mandated Should … subtest naming. One shared root cause: cases are either named descriptively without the Should prefix or run directly in the parent test body. As per path instructions, "MUST use t.Run("Should...") pattern for ALL test cases".

  • internal/core/workspace/config_test.go#L1661-L1699: rename the table-case names ("normal under defaults", etc.) to Should … phrasing; do the same for the precedence table at L2325-L2390.
  • internal/api/contract/contract_integration_test.go#L165-L167: rename to "Should keep run list and detail shape compatible".
  • internal/api/httpapi/openapi_contract_test.go#L329-L347: move the SpeedResolution/RunJobSummary assertions into Should … subtests, and wrap TestGeneratedOpenAPIContractKeepsSpeedFieldsOptionalAndLiteral (L350-L373) in one as well.
  • internal/api/client/client_contract_test.go#L501-L530: wrap the historical-snapshot body in a Should decode historical snapshots without speed fields subtest.
🤖 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/workspace/config_test.go` around lines 1661 - 1699, Update the
speed-cohort tests to follow the mandated Should-subtest convention: in
internal/core/workspace/config_test.go ranges 1661-1699 and 2325-2390, rename
every table case with Should-prefixed phrasing; in
internal/api/contract/contract_integration_test.go range 165-167, rename the
case to “Should keep run list and detail shape compatible”; in
internal/api/httpapi/openapi_contract_test.go range 329-347, move the
SpeedResolution and RunJobSummary assertions into Should subtests, and wrap
TestGeneratedOpenAPIContractKeepsSpeedFieldsOptionalAndLiteral in a Should
subtest; in internal/api/client/client_contract_test.go range 501-530, wrap the
historical-snapshot assertions in a “Should decode historical snapshots without
speed fields” subtest.

Source: Path instructions

internal/api/contract/contract_integration_test.go (1)

199-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Negative substring assertion is broad but acceptable; consider asserting on the decoded run summary instead.

strings.Contains(body, "\"speed\"") guards shape stability, yet it would also fail if any unrelated nested payload legitimately gains a speed key later. Decoding into a map[string]any and checking the run objects keeps the intent narrower.

🤖 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/contract/contract_integration_test.go` around lines 199 - 205,
Refine the response-shape assertion in the integration test around the existing
response body check: decode the response JSON into a map or equivalent
structure, inspect only the run summary objects, and assert that those objects
do not contain the “speed” field. Preserve the current failure context using
testCase.path and the response details.
internal/core/agent/client_test.go (2)

273-344: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New speed tests do not follow the Should … subtest convention. The shared root cause is that the tests added in this PR register table cases (or omit subtests entirely) without the repository-mandated Should … naming, which makes failures harder to scan against the rest of the suites.

  • internal/core/agent/client_test.go#L273-L344: rename the TestClientCreateSessionSpeedOutcomes case names ("fast applied after write", "unsupported absent", …) to "Should …" phrasing; apply the same to TestClientAtomicSessionSetupOrdersModelSpeedAndPrompt at L162-L176.
  • internal/cli/daemon_commands_test.go#L3594-L3613: rename the three table entries ("omitted speed leaves the payload empty", "explicit normal remains present", "explicit fast remains present") to "Should …" phrasing.
  • internal/core/run/executor/execution_acp_test.go#L33-L51: wrap the three assertions of TestHookRuntimeConfigPreservesSpeed in t.Run("Should …", …) subtests.

As per path instructions, "MUST use t.Run("Should...") pattern for ALL test cases".

🤖 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/agent/client_test.go` around lines 273 - 344, Rename all
table-driven test case names in internal/core/agent/client_test.go (lines
273-344, including TestClientCreateSessionSpeedOutcomes and
TestClientAtomicSessionSetupOrdersModelSpeedAndPrompt) to use “Should …”
phrasing. Rename the three table entries in internal/cli/daemon_commands_test.go
(lines 3594-3613) similarly. In internal/core/run/executor/execution_acp_test.go
(lines 33-51), wrap each assertion in TestHookRuntimeConfigPreservesSpeed with a
t.Run subtest named “Should …”.

Source: Path instructions


3062-3097: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Capability-claim assertion partially duplicates the DeepEqual check.

The reflect.DeepEqual comparison against the expected ClientCapabilities already rejects any extra claim; the subsequent substring scan over the marshaled JSON ("session", "configOptions", "boolean", "_meta") is a weaker, string-based restatement that will silently stop protecting anything if field names change. Consider dropping it, or keeping only the JSON check with a comment explaining the wire-shape invariant it guards.

🤖 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/agent/client_test.go` around lines 3062 - 3097, Remove the
redundant marshaling and substring checks from helperAgent.Initialize after the
reflect.DeepEqual comparison, including the associated error handling and
RequestError path. Keep the exact ClientCapabilities comparison as the sole
assertion unless a focused JSON check is required to enforce a distinct
wire-format invariant.
internal/core/run/parallel/resolver_test.go (1)

719-737: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused conflict-session helpers.

conflictAtomicClient, newConflictAtomicSession, installConflictRuntimeProbeStub, and swapConflictAtomicClient are only definitions in internal/core/run/parallel/resolver_test.go; no tested path installs or uses them, so they should be removed.

🤖 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/parallel/resolver_test.go` around lines 719 - 737, Remove
the unused test-only helpers conflictAtomicClient, newConflictAtomicSession,
installConflictRuntimeProbeStub, and swapConflictAtomicClient from
resolver_test.go. Delete their associated fields and methods as well, without
changing any active resolver tests or production behavior.
internal/cli/root_command_execution_test.go (1)

425-480: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

New speed tests don't use the required t.Run("Should ...") subtest pattern.

Path instructions for **/*_test.go state tests MUST use t.Run("Should...") pattern for ALL test cases. All seven newly-introduced test functions below use descriptive-but-non-"Should..." subtest names, or skip subtests entirely, deviating from this rule (even though it mirrors much of this codebase's pre-existing local convention).

  • internal/cli/root_command_execution_test.go#L425-L480: wrap TestExecCommandRejectsConflictingExplicitResumeSpeedBeforeSubmission's body in t.Run("Should reject a conflicting explicit --speed during resume", func(t *testing.T) {...}).
  • internal/cli/root_test.go#L648-L733: rename each t.Run(tc.path, ...) subtest to a "Should ..." description (e.g., "Should document speed contract in " + tc.path).
  • internal/cli/root_test.go#L1014-L1040: rename t.Run(tc.name, ...) to "Should register --speed for " + tc.name.
  • internal/cli/root_test.go#L1042-L1077: rename t.Run(string(tc.kind), ...) to a "Should ..." description per command kind.
  • internal/cli/root_test.go#L1079-L1105: rename t.Run(tc.name, ...) to "Should reject " + tc.name.
  • internal/cli/root_test.go#L1107-L1152: rename t.Run(tc.name, ...) to a "Should ..." description per case.
  • internal/core/agents/mcpserver/engine_test.go#L169-L258: wrap TestRunAgentInheritsSpeedAcrossIndependentChildExecutions's body in t.Run("Should keep child runtime speed independent across executions", func(t *testing.T) {...}).
🤖 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/cli/root_command_execution_test.go` around lines 425 - 480, Update
all seven listed test sites to use subtest names beginning with “Should”: wrap
TestExecCommandRejectsConflictingExplicitResumeSpeedBeforeSubmission and
TestRunAgentInheritsSpeedAcrossIndependentChildExecutions in the specified
Should-named t.Run blocks; rename the table-driven subtests in
internal/cli/root_test.go at ranges 648-733, 1014-1040, 1042-1077, 1079-1105,
and 1107-1152 to descriptive Should-prefixed names while preserving their
existing test logic.

Source: Path instructions

internal/core/run/ui/update.go (1)

888-924: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Attempt gating is skipped when Resolution is nil.

A stale jobSpeedMsg carrying only Requested (no resolution) bypasses the attempt < job.speedResolutionAttempt guard and can overwrite job.requestedSpeed for a newer attempt. Consider applying the same gate before the v.Requested assignment.

♻️ Proposed adjustment
 	attempt := v.Attempt
+	if attempt <= 0 {
+		attempt = job.attempt
+	}
+	if attempt > 0 && attempt < job.speedResolutionAttempt {
+		return
+	}
 	if v.Resolution != nil {
-		if attempt <= 0 {
-			attempt = job.attempt
-		}
-		if attempt > 0 && attempt < job.speedResolutionAttempt {
-			return
-		}
-	}
+		job.speedResolutionAttempt = attempt
+	}
🤖 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 888 - 924, Update
uiModel.handleJobSpeed to apply attempt gating whenever v.Attempt identifies an
older attempt, including messages with a nil Resolution, before assigning
v.Requested to job.requestedSpeed. Preserve the existing fallback to job.attempt
and ensure stale requested-only messages return without mutating job state.
pkg/compozy/events/kinds/speed.go (1)

1-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider colocating a canonical validator with the type.

Speed is validated in at least the CLI/workspace-config layers; exposing func (s Speed) Valid() bool (or ParseSpeed(string) (Speed, error)) here would keep the accepted set in one place as more surfaces (HTTP, UDS, SDK) start accepting speed input.

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

In `@pkg/compozy/events/kinds/speed.go` around lines 1 - 44, Add a canonical
validation API alongside the Speed type, preferably Speed.Valid() or ParseSpeed,
that accepts only the defined SpeedNormal and SpeedFast values. Update existing
CLI and workspace-config validation to reuse this API so all input surfaces
share one accepted-value set.
internal/core/run/executor/execution_acp_integration_test.go (1)

205-205: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer an explicit conditional over the map[bool]string{...}[cond] trick.

map[bool]string{true: "sess-speed"}[test.resume] allocates a map to express an if/else and reads poorly next to the surrounding literal fields.

♻️ Suggested rewrite
-				ExpectedLoadSessionID:    map[bool]string{true: "sess-speed"}[test.resume],
+				ExpectedLoadSessionID:    expectedLoadSessionID(test.resume),
func expectedLoadSessionID(resume bool) string {
	if resume {
		return "sess-speed"
	}
	return ""
}
🤖 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/executor/execution_acp_integration_test.go` at line 205,
Replace the map-based ExpectedLoadSessionID expression in the test case literal
with an explicit conditional helper or equivalent if/else logic: return
"sess-speed" when test.resume is true and an empty string otherwise. Keep the
surrounding expected literal fields unchanged.
internal/core/agent/speed_config.go (1)

14-18: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Keep the iota block for shapes only.

speedConfigCategoryModelConfig is a different type and sits inside the speedConfigShape iota block; a future shape constant inserted above it would silently shift values. Declaring the category in its own const block removes that footgun.

♻️ Suggested split
 const (
 	speedConfigShapeBoolean speedConfigShape = iota + 1
 	speedConfigShapeSelect
-	speedConfigCategoryModelConfig = acp.SessionConfigOptionCategory("model_config")
 )
+
+const speedConfigCategoryModelConfig = acp.SessionConfigOptionCategory("model_config")
🤖 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/agent/speed_config.go` around lines 14 - 18, Move
speedConfigCategoryModelConfig out of the iota-based const block into its own
const declaration, leaving only speedConfigShapeBoolean and
speedConfigShapeSelect in the speedConfigShape block so future shape constants
cannot alter the category value.
internal/core/run/executor/lifecycle.go (1)

302-315: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate zero-value-to-nil helper.

optionalSpeedResolution here duplicates optionalExecSpeedResolution in internal/core/run/exec/exec.go. Will address via consolidated comment.

🤖 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/executor/lifecycle.go` around lines 302 - 315, Remove the
duplicate optionalSpeedResolution helper from the lifecycle code and reuse the
existing optionalExecSpeedResolution helper from the exec package, updating
optionalRejectedSpeedResolution accordingly while preserving its rejected-status
filtering and zero-value-to-nil behavior.
internal/core/run/exec/exec.go (1)

1017-1023: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate zero-value-to-nil helper.

optionalExecSpeedResolution is a byte-for-byte duplicate of optionalSpeedResolution in internal/core/run/executor/lifecycle.go. Given how many layers in this PR need this exact conversion, consolidating into a shared exported helper (e.g., on kinds.SpeedResolution) would avoid drift.

🤖 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/exec/exec.go` around lines 1017 - 1023, Replace the
duplicate optionalExecSpeedResolution helper with a shared exported
zero-value-to-nil conversion helper associated with kinds.SpeedResolution, then
update both exec.go and lifecycle.go callers to use it and remove the local
helpers. Preserve the existing behavior of returning nil for an empty resolution
and a copied pointer for non-zero values.
internal/core/run/internal/acpshared/command_io.go (2)

214-226: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the always-empty option_shape log field.

"option_shape", "" never carries a value, so it only adds noise to structured logs. Either drop it or populate it from the resolution source.

♻️ Proposed cleanup
 	logger.Info(
 		"ACP session speed resolved",
 		"speed_requested", resolution.Requested,
 		"speed_status", resolution.Status,
 		"speed_reason", resolution.Reason,
-		"option_shape", "",
 		"resumed", job != nil && strings.TrimSpace(job.ResumeSession) != "",
 	)
🤖 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/internal/acpshared/command_io.go` around lines 214 - 226,
Remove the always-empty "option_shape" field from the structured logging
arguments in logSpeedResolution, leaving the existing speed resolution and
resumed-session fields unchanged.

236-257: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Collapse the duplicated write/error-wrap branches.

♻️ Proposed simplification
-	if resolution.Reason == "" {
-		if _, err := fmt.Fprintf(dst, "Speed %s: %s\n", resolution.Requested, resolution.Status); err != nil {
-			return fmt.Errorf("write speed resolution status: %w", err)
-		}
-		return nil
-	}
-	if _, err := fmt.Fprintf(
-		dst,
-		"Speed %s: %s (%s)\n",
-		resolution.Requested,
-		resolution.Status,
-		resolution.Reason,
-	); err != nil {
-		return fmt.Errorf("write speed resolution status: %w", err)
-	}
-	return nil
+	line := fmt.Sprintf("Speed %s: %s", resolution.Requested, resolution.Status)
+	if resolution.Reason != "" {
+		line += fmt.Sprintf(" (%s)", resolution.Reason)
+	}
+	if _, err := fmt.Fprintln(dst, line); err != nil {
+		return fmt.Errorf("write speed resolution status: %w", err)
+	}
+	return nil
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/core/run/internal/acpshared/command_io.go` around lines 236 - 257,
Collapse the duplicated fmt.Fprintf and error-wrapping branches in
WriteSpeedResolutionStatus by constructing the output format or message based on
whether resolution.Reason is empty, then performing one write and one “write
speed resolution status” error-wrap path. Preserve the current output for both
reason-present and reason-absent cases.
pkg/compozy/runs/run.go (1)

447-452: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document the index reconciliation rule.

runJobIndex silently prefers Summary.Index only when job.Index == 0, which is indistinguishable from a legitimate job at index 0 whose summary index is also 0. It works for the current payloads (state index omitted, summary index present), but a one-line comment stating that the state index is authoritative and the summary index is the fallback for payloads that omit it would prevent future misreadings.

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

In `@pkg/compozy/runs/run.go` around lines 447 - 452, Document the reconciliation
rule directly above or within runJobIndex: job.Index is authoritative when
present, while the nonzero Summary.Index is used only as the fallback when the
state index is omitted. Keep the existing behavior unchanged.
internal/core/run/parallel/orchestrator_test.go (1)

91-150: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Wrap the scenario in a Should … subtest.

This new top-level test asserts a single behavior but doesn't use the repo's t.Run("Should …") convention that the rest of the suite follows (e.g. the helper-based scenarios below). t.Setenv correctly rules out t.Parallel, so only the naming needs adjusting.

As per coding guidelines: "Go tests must use Should … subtest names".

🤖 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/parallel/orchestrator_test.go` around lines 91 - 150, The
TestParallelExecutionOrchestratorConflictSpeedIntegration test should run its
existing scenario inside a t.Run subtest named with the repository’s “Should …”
convention. Preserve the setup, assertions, and environment handling unchanged,
and do not add t.Parallel.

Source: Coding guidelines

sdk/extension/types.go (1)

523-571: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

sdk/extension/types.go is well past the 500-line cap and keeps growing.

This change extends an already oversized contracts file (RuntimeConfig alone ends at Line 571). Not introduced by this PR, but a good moment to split the session/request contracts from the runtime-config contracts into separate files.

As per coding guidelines: "Production Go files must have one responsibility and remain at or below 500 lines; split contracts, registries, implementations, and cross-cutting helpers into separate files."

🤖 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 `@sdk/extension/types.go` around lines 523 - 571, Split the oversized contracts
file so session/request contract definitions are separate from
runtime-configuration contracts, keeping each production Go file at or below 500
lines. Move RuntimeConfig and its related runtime symbols into the dedicated
runtime-config file while preserving package visibility and existing references;
leave unrelated contracts in their appropriate file.

Source: Coding guidelines

internal/daemon/run_snapshot_test.go (1)

526-559: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Split the two invariants into separate tests.

This test covers both snapshot builder historical-absence projection and cloneRunJobSummary deep-copy independence; a failure in one obscures the other. Splitting (and using t.Run("Should …") subtests) keeps ownership clear.

As per coding guidelines: "Go tests must use Should … subtest names".

🤖 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 526 - 559, Split
TestRunSnapshotBuilderPreservesHistoricalSpeedAbsenceAndClonesResolution into
separate tests: one covering historical speed absence from
newRunSnapshotBuilder/applyEvent and assertRunSnapshotSpeed, and another
covering cloneRunJobSummary deep-copy independence. Use t.Run subtests with
names beginning “Should …” so each invariant reports failures independently and
follows the repository’s Go test naming guideline.

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/cli/daemon_commands.go`:
- Around line 1531-1534: Update the speed override branch in the daemon command
flow to preserve all flags captured by captureExplicitRuntimeFlags, including
IDE, Model, ReasoningEffort, and AccessMode, while setting Speed to true. Reuse
the existing helper used by the review exec paths or copy the captured flags
before applying the speed override; do not emit a struct containing only Speed.
- Around line 1492-1495: Update the daemon override handling around the speed
setter and the corresponding flow in reviews_exec_daemon.go to validate s.speed
with parseRuntimeSpeed before assigning overrides.Speed. Preserve valid speed
serialization, and make invalid values consistently produce the deterministic
invalid_runtime_overrides error instead of serializing an unchecked kinds.Speed.

In `@internal/core/agent/client.go`:
- Around line 419-438: The agent.post_session_create hook is dispatched before
completeSessionStart can fail and remove the session. Move
dispatchPostSessionCreateHook after completeSessionStart returns successfully,
before launchRunPrompt, so observers are notified only when the returned
SessionStart is valid.
- Around line 663-670: Update the model-stage error branch in the
speed-resolution flow around SessionSetupStageSetModel so it does not return a
SpeedResolution with an empty Status. Return the established
zero-value/no-resolution result or the canonical rejected/unsupported status
used by the other failure paths, while preserving the existing wrapped ACP setup
error.

In `@internal/core/agent/speed_config_test.go`:
- Around line 20-27: Rename all affected Go test subtests to begin with “Should
…”: in internal/core/agent/speed_config_test.go lines 20-27 and other tables in
that file, use descriptive Should names; in
internal/core/agent/session_helpers_test.go lines 38-66 and 89-110, rename
option and response cases accordingly; in internal/core/model/model_test.go
lines 694-700, rename explicit speed cases and wrap
TestRuntimeConfigLegacyJSONDefaultsSpeed in a Should subtest; in
internal/core/run/exec/exec_integration_test.go lines 378-388, rename status
cases and add Should subtests to the two new top-level speed tests; and in
internal/core/run/executor/execution_acp_integration_test.go lines 134-186,
rename workflow cases with the required prefix.

In `@internal/core/model/model_test.go`:
- Around line 844-846: Update requireCanonicalSpeed to validate that the
supplied speed is either normal or fast, failing the test for any other value;
alternatively remove the no-op helper and compare cloned.Speed directly at its
call sites. Do not retain a helper that merely returns its input while implying
validation.

In `@internal/core/run/exec/exec_integration_test.go`:
- Around line 154-180: Remove t.Fatalf calls from the createSessionFn and
resumeSessionFn callbacks in this test. Validate the requested speed and session
ID by returning an error from the callbacks or capturing the requests for
assertions after ExecuteExec/createACPSession returns, ensuring callback
failures are reported safely from the test goroutine.

In `@internal/core/run/exec/exec_test.go`:
- Around line 207-247: Add t.Parallel() at the beginning of
TestExecRunStateEmitsCanceledSpeedOutcome, alongside the existing setup, so this
test follows the repository’s default parallel execution convention.
- Around line 96-129: Update the testCases names used by the subtests in the
surrounding test function so every case follows the mandated “Should ...” naming
pattern. Rename the “applied”, “unsupported”, and “rejected” values in tc.name
without changing their resolutions, expected statuses, or errors.

In `@internal/core/run/exec/exec.go`:
- Around line 40-107: Split the oversized exec implementation into focused files
within the same package: move PersistedExecRun, persistedExecTurn, execEvent,
and execEventSession into a contracts/types file; move execEventWriter and
execEventEmitter emission plumbing into a dedicated event file; and keep
attempt/execution logic separate. Ensure each production Go file stays under 500
lines while preserving existing package-level APIs and behavior.

In `@internal/core/run/executor/execution_test.go`:
- Around line 1625-1654: Add t.Parallel() at the start of
TestEmitRunStartCarriesRequestedSpeed, following the repository’s Go test
convention; keep the existing test setup and assertions unchanged.

In `@internal/core/run/internal/acpshared/command_io_test.go`:
- Around line 126-152: Rename every affected test case to use the required
t.Run("Should …") subtest naming pattern: update the applied, unsupported,
rejected, WriteSpeedResolutionStatus, HumanStatusWriter, and lifecycle cases in
internal/core/run/internal/acpshared/command_io_test.go#L126-L152; split the
three assertions into separate Should … subtests in
internal/core/run/internal/runshared/config_test.go#L10-L35; and rename the
marshal/decode table cases in
pkg/compozy/events/kinds/payload_compat_test.go#L87-L149.

In `@internal/core/run/ui/speed_test.go`:
- Around line 103-187: Rename all new Go test subtests to use the mandated
“Should …” prefix: in internal/core/run/ui/speed_test.go lines 103-187, update
status cases and trailing subtests and use speedStatusPending instead of the
literal “pending”; in internal/cli/workspace_config_test.go lines 1262-1314,
rename both precedence subtests to “Should prefer …”; in
internal/daemon/task_multi_test.go lines 34-174, rename all listed cases; in
lines 648-689, split the flat test into “Should …” subtests for nil-child,
independent-clone, and no-mutation invariants; and in
pkg/compozy/events/kinds/speed_test.go lines 16-40, rename literal and JSON
contract cases accordingly.

In `@internal/core/workspace/config_validate.go`:
- Around line 544-561: Update validateRuntimeSpeed to normalize cfg.Speed in
place by trimming its pointed-to value before validation, changing the value
receiver or handling the normalization during the surrounding config merge if
necessary. Preserve acceptance of "normal" and "fast" values while ensuring
commandState.speed receives the canonical trimmed value.

In `@internal/daemon/run_manager_test.go`:
- Around line 1720-1831: Rename the three subtests in
internal/daemon/run_manager_test.go:1720-1831 to “Should preserve configured
speed when omitted for task and review”, “Should apply explicit speed last for
task and review”, and “Should apply persisted intent then explicit override in
order for exec”. Rename the table entries in
internal/daemon/review_watch_test.go:59-98 to “Should ...” sentence-form names,
including “Should inject retry defaults with explicit normal speed”. Rename the
“normal” and “fast” entries in internal/daemon/review_watch_test.go:111-158 to
“Should decode parent speed normal” and “Should decode parent speed fast”.
- Around line 139-145: Handle the error returned by runDB.Close() in the defer
following openRunDBForRunID, replacing the discarded `_ =` result with the
test’s established failure-reporting mechanism while preserving cleanup
execution.

In `@pkg/compozy/events/kinds/docs_test.go`:
- Around line 145-155: Update the subtest declaration in the section-validation
loop to use a descriptive name beginning with “Should ...” instead of passing
section.start directly to t.Run. Preserve the existing section-specific context
in the name and keep the test body and parallel execution unchanged.

In `@pkg/compozy/runs/summary.go`:
- Around line 62-92: Update the List hydration loop and hydrateRunSummaryDetails
flow so an OpenRun failure for one summary preserves that original summary and
continues processing the remaining runs instead of returning the error. Avoid
making listing depend on every run snapshot being readable; if hydration remains
enabled, execute it through a bounded worker pool rather than sequentially
issuing one request per run.

In `@test/skills_bundle_test.go`:
- Around line 211-226: Update the subtest declaration in the test case loop to
use the mandated t.Run("Should ...", ...) naming pattern instead of tc.path
directly. Preserve tc.path in the failure messages and keep the existing
parallel execution and content validation behavior unchanged.

---

Outside diff comments:
In `@internal/core/run/internal/acpshared/command_io.go`:
- Around line 347-364: Update the session-started runtime event construction in
the surrounding command function to pass nil for a zero-value
kinds.SpeedResolution and a pointer only when it contains resolved data. Reuse
or add a small speedResolutionPointer helper for this conversion, then assign
its result to SpeedResolution so downstream consumers continue using nil to
detect absent speed information.

---

Nitpick comments:
In `@internal/api/contract/contract_integration_test.go`:
- Around line 199-205: Refine the response-shape assertion in the integration
test around the existing response body check: decode the response JSON into a
map or equivalent structure, inspect only the run summary objects, and assert
that those objects do not contain the “speed” field. Preserve the current
failure context using testCase.path and the response details.

In `@internal/api/httpapi/openapi_contract_test.go`:
- Around line 541-558: Update schemaEnumStrings to sort values with slices.Sort
instead of sort.Strings, reusing the file’s existing slices import; then remove
the sort import if no other references remain in the file.
- Around line 350-373: Update
TestGeneratedOpenAPIContractKeepsSpeedFieldsOptionalAndLiteral to avoid exact
generated TypeScript fragment matching: use a Should subtest and assertions that
each Speed* type contains the expected union members order-independently, while
separately verifying speed and speed_resolution are optional and
requested/status are required. Preserve the existing canonical literals and
remove dependence on generator spacing, ordering, or formatting.

In `@internal/cli/root_command_execution_test.go`:
- Around line 425-480: Update all seven listed test sites to use subtest names
beginning with “Should”: wrap
TestExecCommandRejectsConflictingExplicitResumeSpeedBeforeSubmission and
TestRunAgentInheritsSpeedAcrossIndependentChildExecutions in the specified
Should-named t.Run blocks; rename the table-driven subtests in
internal/cli/root_test.go at ranges 648-733, 1014-1040, 1042-1077, 1079-1105,
and 1107-1152 to descriptive Should-prefixed names while preserving their
existing test logic.

In `@internal/core/agent/client_test.go`:
- Around line 273-344: Rename all table-driven test case names in
internal/core/agent/client_test.go (lines 273-344, including
TestClientCreateSessionSpeedOutcomes and
TestClientAtomicSessionSetupOrdersModelSpeedAndPrompt) to use “Should …”
phrasing. Rename the three table entries in internal/cli/daemon_commands_test.go
(lines 3594-3613) similarly. In internal/core/run/executor/execution_acp_test.go
(lines 33-51), wrap each assertion in TestHookRuntimeConfigPreservesSpeed with a
t.Run subtest named “Should …”.
- Around line 3062-3097: Remove the redundant marshaling and substring checks
from helperAgent.Initialize after the reflect.DeepEqual comparison, including
the associated error handling and RequestError path. Keep the exact
ClientCapabilities comparison as the sole assertion unless a focused JSON check
is required to enforce a distinct wire-format invariant.

In `@internal/core/agent/speed_config.go`:
- Around line 14-18: Move speedConfigCategoryModelConfig out of the iota-based
const block into its own const declaration, leaving only speedConfigShapeBoolean
and speedConfigShapeSelect in the speedConfigShape block so future shape
constants cannot alter the category value.

In `@internal/core/run/exec/exec.go`:
- Around line 1017-1023: Replace the duplicate optionalExecSpeedResolution
helper with a shared exported zero-value-to-nil conversion helper associated
with kinds.SpeedResolution, then update both exec.go and lifecycle.go callers to
use it and remove the local helpers. Preserve the existing behavior of returning
nil for an empty resolution and a copied pointer for non-zero values.

In `@internal/core/run/executor/execution_acp_integration_test.go`:
- Line 205: Replace the map-based ExpectedLoadSessionID expression in the test
case literal with an explicit conditional helper or equivalent if/else logic:
return "sess-speed" when test.resume is true and an empty string otherwise. Keep
the surrounding expected literal fields unchanged.

In `@internal/core/run/executor/lifecycle.go`:
- Around line 302-315: Remove the duplicate optionalSpeedResolution helper from
the lifecycle code and reuse the existing optionalExecSpeedResolution helper
from the exec package, updating optionalRejectedSpeedResolution accordingly
while preserving its rejected-status filtering and zero-value-to-nil behavior.

In `@internal/core/run/internal/acpshared/command_io.go`:
- Around line 214-226: Remove the always-empty "option_shape" field from the
structured logging arguments in logSpeedResolution, leaving the existing speed
resolution and resumed-session fields unchanged.
- Around line 236-257: Collapse the duplicated fmt.Fprintf and error-wrapping
branches in WriteSpeedResolutionStatus by constructing the output format or
message based on whether resolution.Reason is empty, then performing one write
and one “write speed resolution status” error-wrap path. Preserve the current
output for both reason-present and reason-absent cases.

In `@internal/core/run/parallel/orchestrator_test.go`:
- Around line 91-150: The
TestParallelExecutionOrchestratorConflictSpeedIntegration test should run its
existing scenario inside a t.Run subtest named with the repository’s “Should …”
convention. Preserve the setup, assertions, and environment handling unchanged,
and do not add t.Parallel.

In `@internal/core/run/parallel/resolver_test.go`:
- Around line 719-737: Remove the unused test-only helpers conflictAtomicClient,
newConflictAtomicSession, installConflictRuntimeProbeStub, and
swapConflictAtomicClient from resolver_test.go. Delete their associated fields
and methods as well, without changing any active resolver tests or production
behavior.

In `@internal/core/run/ui/update.go`:
- Around line 888-924: Update uiModel.handleJobSpeed to apply attempt gating
whenever v.Attempt identifies an older attempt, including messages with a nil
Resolution, before assigning v.Requested to job.requestedSpeed. Preserve the
existing fallback to job.attempt and ensure stale requested-only messages return
without mutating job state.

In `@internal/core/workspace/config_test.go`:
- Around line 1841-1844: Update the error assertion in the strict-mode workspace
configuration test to verify the stable “decode workspace config:” wrapper
prefix and use strings.Contains to check for “strict mode” rather than comparing
the TOML decoder’s full message. Preserve the existing failure reporting and
ensure the test imports strings if needed.
- Around line 1661-1699: Update the speed-cohort tests to follow the mandated
Should-subtest convention: in internal/core/workspace/config_test.go ranges
1661-1699 and 2325-2390, rename every table case with Should-prefixed phrasing;
in internal/api/contract/contract_integration_test.go range 165-167, rename the
case to “Should keep run list and detail shape compatible”; in
internal/api/httpapi/openapi_contract_test.go range 329-347, move the
SpeedResolution and RunJobSummary assertions into Should subtests, and wrap
TestGeneratedOpenAPIContractKeepsSpeedFieldsOptionalAndLiteral in a Should
subtest; in internal/api/client/client_contract_test.go range 501-530, wrap the
historical-snapshot assertions in a “Should decode historical snapshots without
speed fields” subtest.

In `@internal/daemon/run_snapshot_test.go`:
- Around line 526-559: Split
TestRunSnapshotBuilderPreservesHistoricalSpeedAbsenceAndClonesResolution into
separate tests: one covering historical speed absence from
newRunSnapshotBuilder/applyEvent and assertRunSnapshotSpeed, and another
covering cloneRunJobSummary deep-copy independence. Use t.Run subtests with
names beginning “Should …” so each invariant reports failures independently and
follows the repository’s Go test naming guideline.

In `@pkg/compozy/events/kinds/speed.go`:
- Around line 1-44: Add a canonical validation API alongside the Speed type,
preferably Speed.Valid() or ParseSpeed, that accepts only the defined
SpeedNormal and SpeedFast values. Update existing CLI and workspace-config
validation to reuse this API so all input surfaces share one accepted-value set.

In `@pkg/compozy/runs/run.go`:
- Around line 447-452: Document the reconciliation rule directly above or within
runJobIndex: job.Index is authoritative when present, while the nonzero
Summary.Index is used only as the fallback when the state index is omitted. Keep
the existing behavior unchanged.

In `@sdk/extension/types.go`:
- Around line 523-571: Split the oversized contracts file so session/request
contract definitions are separate from runtime-configuration contracts, keeping
each production Go file at or below 500 lines. Move RuntimeConfig and its
related runtime symbols into the dedicated runtime-config file while preserving
package visibility and existing references; leave unrelated contracts in their
appropriate file.
🪄 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 Plus

Run ID: d4f54158-5801-425b-b5a6-d358cc1bdbb3

📥 Commits

Reviewing files that changed from the base of the PR and between 623f473 and ef44e8c.

⛔ Files ignored due to path filters (9)
  • CHANGELOG.md is excluded by !**/*.md
  • README.md is excluded by !**/*.md
  • docs/events.md is excluded by !**/*.md
  • docs/reader-library.md is excluded by !**/*.md
  • openapi/compozy-daemon.json is excluded by !**/*.json
  • skills/compozy/SKILL.md is excluded by !**/*.md
  • skills/compozy/references/cli-reference.md is excluded by !**/*.md
  • skills/compozy/references/config-reference.md is excluded by !**/*.md
  • web/src/generated/compozy-openapi.d.ts is excluded by !**/generated/**, !**/generated/**, !**/*.d.ts
📒 Files selected for processing (96)
  • internal/api/client/client_contract_test.go
  • internal/api/contract/contract_integration_test.go
  • internal/api/contract/contract_test.go
  • internal/api/contract/types.go
  • internal/api/httpapi/openapi_contract_test.go
  • internal/cli/daemon_commands.go
  • internal/cli/daemon_commands_test.go
  • internal/cli/reusable_agents_doc_examples_test.go
  • internal/cli/reviews_exec_daemon.go
  • internal/cli/reviews_exec_daemon_additional_test.go
  • internal/cli/root_command_execution_test.go
  • internal/cli/root_test.go
  • internal/cli/state.go
  • internal/cli/testdata/exec_help.golden
  • internal/cli/testdata/tasks_run_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/codex_live_test.go
  • internal/core/agent/hooks.go
  • internal/core/agent/registry_validate.go
  • internal/core/agent/session_helpers_test.go
  • internal/core/agent/session_test.go
  • internal/core/agent/speed_config.go
  • internal/core/agent/speed_config_test.go
  • internal/core/agents/mcpserver/engine_test.go
  • internal/core/agents/session_mcp.go
  • internal/core/agents/session_mcp_test.go
  • internal/core/api.go
  • internal/core/api_test.go
  • internal/core/kernel/commands/commands_test.go
  • internal/core/kernel/recovery_prepared_run_test.go
  • internal/core/model/model_test.go
  • internal/core/model/runtime_config.go
  • internal/core/run/exec/aliases.go
  • internal/core/run/exec/exec.go
  • internal/core/run/exec/exec_integration_test.go
  • internal/core/run/exec/exec_test.go
  • internal/core/run/exec/run_agent_engine_integration_test.go
  • internal/core/run/executor/execution.go
  • internal/core/run/executor/execution_acp_integration_test.go
  • internal/core/run/executor/execution_acp_test.go
  • internal/core/run/executor/execution_test.go
  • internal/core/run/executor/hooks.go
  • internal/core/run/executor/lifecycle.go
  • internal/core/run/executor/result.go
  • internal/core/run/executor/result_test.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/config_test.go
  • internal/core/run/parallel/orchestrator.go
  • internal/core/run/parallel/orchestrator_test.go
  • internal/core/run/parallel/resolver.go
  • internal/core/run/parallel/resolver_test.go
  • internal/core/run/recovery/agentic_test.go
  • internal/core/run/ui/model.go
  • internal/core/run/ui/multi_remote.go
  • internal/core/run/ui/remote.go
  • internal/core/run/ui/sidebar.go
  • internal/core/run/ui/speed_test.go
  • internal/core/run/ui/types.go
  • internal/core/run/ui/update.go
  • internal/core/workspace/config_merge.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
  • internal/core/workspace/config_validate.go
  • internal/daemon/recovery_run.go
  • internal/daemon/review_watch_test.go
  • internal/daemon/run_manager.go
  • internal/daemon/run_manager_test.go
  • internal/daemon/run_snapshot.go
  • internal/daemon/run_snapshot_compact.go
  • internal/daemon/run_snapshot_test.go
  • internal/daemon/task_multi.go
  • internal/daemon/task_multi_test.go
  • pkg/compozy/events/kinds/docs_test.go
  • pkg/compozy/events/kinds/job.go
  • pkg/compozy/events/kinds/payload_compat_test.go
  • pkg/compozy/events/kinds/run.go
  • pkg/compozy/events/kinds/session.go
  • pkg/compozy/events/kinds/speed.go
  • pkg/compozy/events/kinds/speed_test.go
  • pkg/compozy/runs/examples_test.go
  • pkg/compozy/runs/helpers_test.go
  • pkg/compozy/runs/integration_test.go
  • pkg/compozy/runs/run.go
  • pkg/compozy/runs/summary.go
  • pkg/compozy/runs/watch.go
  • scripts/inspect-acp-toolcalls.go
  • sdk/extension/types.go
  • sdk/extension/types_test.go
  • test/skills_bundle_test.go

Comment thread internal/cli/daemon_commands.go Outdated
Comment thread internal/cli/daemon_commands.go Outdated
Comment thread internal/core/agent/client.go Outdated
Comment thread internal/core/agent/client.go Outdated
Comment thread internal/core/agent/speed_config_test.go Outdated
Comment thread internal/daemon/run_manager_test.go Outdated
Comment thread internal/daemon/run_manager_test.go Outdated
Comment thread pkg/compozy/events/kinds/docs_test.go Outdated
Comment thread pkg/compozy/runs/summary.go Outdated
Comment thread test/skills_bundle_test.go Outdated
@greptile-apps

greptile-apps Bot commented Jul 28, 2026 •

Copy link
Copy Markdown

Greptile Summary

Adds provider-neutral session speed control across the runtime:

  • Introduces canonical normal and fast speed values and ACP capability negotiation.
  • Propagates requested speed and resolution through session creation, spawning, persistence, API contracts, CLI output, and generated clients.
  • Adds validation, diagnostics, documentation, and tests for applied, unsupported, and rejected outcomes.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
internal/acp/speed_config.go Implements provider-neutral ACP speed-option matching, atomic application, confirmation, and explicit resolution outcomes.
internal/acp/start_session.go Integrates speed negotiation into session configuration before prompt dispatch.
internal/session/spawn.go Validates explicit child speed preferences and inherits the parent preference when omitted.
internal/session/session_process_state.go Copies the negotiated speed resolution from ACP process capabilities into session state.
internal/store/session_meta_types.go Adds persisted speed metadata and validates consistency between requested speed and its resolution.
internal/api/contract/session_runtime_payloads.go Extends create-session requests and session responses with speed intent and resolution fields.
internal/api/core/conversions.go Hydrates public session payloads with cloned speed-resolution state.
internal/cli/session_create.go Adds the canonical session speed option to the CLI creation flow.
openapi/compozy.json Publishes speed values and resolution outcomes in the generated API contract.

Sequence Diagram

sequenceDiagram
  participant Caller as CLI / API
  participant Manager as Session Manager
  participant ACP as ACP Driver
  participant Provider as ACP Provider
  participant Store as Session Metadata
  Caller->>Manager: Create or spawn session with speed
  Manager->>ACP: Start session with requested speed
  ACP->>Provider: Discover configuration options
  alt Supported and already selected
    ACP-->>Manager: Resolution: applied
  else Supported and requires update
    ACP->>Provider: Set speed option
    Provider-->>ACP: Updated configuration
    ACP-->>Manager: Resolution: applied or rejected
  else Unsupported or ambiguous
    ACP-->>Manager: Resolution: unsupported
  end
  Manager->>Store: Persist requested speed and resolution
  Manager-->>Caller: Expose speed through session payload
Loading

Reviews (3): Last reviewed commit: "fix: align invalid speed test with subte..." | Re-trigger Greptile

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 28, 2026
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.

2 participants