Skip to content

feat: add repo-level default override setup - #90

Merged
pedronauck merged 1 commit into
compozy:mainfrom
camopy:feat/repo-level-default-override-setup
Jun 30, 2026
Merged

pedronauck merged 1 commit into
compozy:mainfrom
camopy:feat/repo-level-default-override-setup

Conversation

@camopy

@camopy camopy commented Apr 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add an optional repo-level config step to compozy setup so interactive setup can override built-in runtime defaults and save those overrides to .compozy/config.toml.

Why

Compozy already works without a repo config file because it has built-in defaults. This change makes repo-level overrides easier to set.

The README already documents workspace config and shows recommended [defaults] values, but setup previously stopped at skill installation. This change closes that gap by letting users change and save repo-specific defaults from the setup flow instead of editing TOML manually.

What Changed

  • added an interactive repo-level defaults step to compozy setup
  • defaulted the override prompt to No
  • limited the config write to workspace scope only
  • skipped the prompt entirely for compozy setup --global
  • only persist defaults after setup completes with zero failures
  • restored IDE selection as a dropdown of supported runtimes
  • used the runtime registry's per-IDE default model as the recommendation source
  • reused the shared built-in default reasoning effort instead of a setup-local hardcoded value
  • preserved existing config sections when writing the selected defaults

Config Written

When the user opts in, setup updates:

[defaults]
ide = "..."
model = "..."
reasoning_effort = "..."

Behavior Notes

  • repo config is written to .compozy/config.toml
  • users who opt out keep the normal built-in defaults with no config change
  • global setup does not prompt for repo defaults and does not modify workspace config
  • if the current model matches the previous recommended model, changing IDE updates the model recommendation to the new IDE default
  • if the repo already has a custom model, setup preserves it

Testing

  • added coverage for registry-backed IDE default model resolution
  • added coverage for preserving unrelated config sections on write
  • added coverage for skipping repo-default prompts in global mode
  • added coverage for not persisting config when setup installation fails

Verification

make verify

Passed locally.

Closes #89

Summary by CodeRabbit

  • New Features

    • Interactive runtime defaults during setup (choose preferred IDE, model, reasoning effort) with preview and persistence to workspace config.
    • Added default reasoning level ("medium") and a lookup for IDE-specific default models.
    • Config read/write updated to omit empty sections when saving and to explicitly handle load/write semantics.
  • Tests

    • Added tests covering runtime-default selection, persistence, config read/write, prompting flows, and context/error propagation.

@coderabbitai

coderabbitai Bot commented Apr 12, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an optional, interactive repo-scoped "runtime defaults" step to compozy setup (skipped for --global and non-interactive): prompts for IDE/model/reasoning-effort, threads a runtimeDefaultsPlan through build → preview/confirm → execute, and on successful install persists the selected defaults to .compozy/config.toml. Tests and workspace config I/O helpers were added/updated.

Changes

Cohort / File(s) Summary
Setup command & tests
internal/cli/setup.go, internal/cli/setup_test.go
Thread a runtimeDefaultsPlan through plan build → preview/confirm → execute; prompt interactive non-global users for IDE/model/reasoning-effort; conditionally render Config/Defaults preview; persist chosen defaults after successful install; extensive tests for prompt, persistence, skipping, error propagation, and context propagation.
Workspace config I/O & types + tests
internal/core/workspace/config.go, internal/core/workspace/config_test.go, internal/core/workspace/config_types.go
Introduce WriteConfig(ctx, configPath, cfg) and LoadConfigFile(ctx, configPath); refactor LoadConfig to use shared loader; make many TOML tags ,omitempty so partially-populated configs omit empty sections; add round-trip test verifying defaults persist and empty sections omitted.
Agent/model defaults
internal/core/agent/registry_specs.go, internal/core/model/runtime_config.go
Add exported DefaultModel(ide string) string helper and export DefaultReasoningEffort constant ("medium") used to recommend runtime defaults.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as Setup Command
    participant Config as Workspace Config
    participant Runtime as Runtime Defaults

    User->>CLI: Run 'compozy setup' (interactive, non-global)
    CLI->>CLI: buildInstallPlan (include runtimeDefaultsPlan)
    CLI->>Config: Load existing workspace config
    Config-->>CLI: return config (or none)
    CLI->>Runtime: Prompt user for IDE/model/reasoning_effort
    User->>Runtime: Choose values / decline
    Runtime-->>CLI: runtimeDefaultsPlan (enabled/disabled + values)
    CLI->>CLI: confirmPlan + executeInstall
    alt install succeeds
        CLI->>Config: Reload config
        CLI->>Config: WriteConfig(.compozy/config.toml) with merged defaults
        Config-->>CLI: write success
        CLI-->>User: Setup complete (defaults saved)
    else install fails
        CLI-->>User: Setup failed (do not persist config)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #40: Modifies workspace config load/write and CLI setup wiring—likely overlapping config I/O and setup flow changes.
  • PR #70: Touches runtime/defaults, model mapping, and CLI/workspace wiring similar to added DefaultReasoningEffort/DefaultModel helpers.
  • PR #60: Changes workspace configuration layer and TOML tags, directly related to ,omitempty and config serialization behavior.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% 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 accurately describes the main change: adding repo-level default override setup to the interactive setup command.
Linked Issues check ✅ Passed All primary objectives from issue #89 are implemented: optional repo-level defaults step after agent/skill selection, writing to config.toml only in workspace scope, skipping for --global mode, not persisting on installation failures, and using registry-backed IDE defaults with built-in reasoning_effort default.
Out of Scope Changes check ✅ Passed All changes align with issue #89 objectives. File modifications are focused on setup command flow, config file handling, workspace config types, and related tests—no unrelated refactoring or feature additions detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
internal/cli/setup.go (1)

123-137: ⚠️ Potential issue | 🟠 Major

Propagate cmd.Context() through the new workspace/config I/O path.

The helpers in the install flow start fresh context.Background() values for workspace resolution, config loads, and config writes. This drops cancellation from the Cobra command exactly where filesystem I/O happens, allowing a canceled setup to continue running through discovery or persistence.

Functions affected:

  • resolveRuntimeDefaultsPlan() (line 279): Uses context.Background() in loadConfigFile call
  • runtimeDefaultsPath() (line 388): Uses context.Background() in resolveWorkspaceContext call
  • persistRuntimeDefaults() (lines 400, 407): Uses context.Background() in both loadConfigFile and writeConfig calls
Suggested fix
-func (s *setupCommandState) buildInstallPlan(
-	cmd *cobra.Command,
+func (s *setupCommandState) buildInstallPlan(
+	ctx context.Context,
+	cmd *cobra.Command,
 	skills []setup.Skill,
 	resolver setup.ResolverOptions,
 	supportedAgents []setup.Agent,
 	detectedAgents []setup.Agent,
 ) (setup.InstallConfig, []setup.PreviewItem, []setup.ReusableAgentPreviewItem, runtimeDefaultsPlan, error) {
@@
-	runtimePlan, err := s.resolveRuntimeDefaultsPlan(globalScope)
+	runtimePlan, err := s.resolveRuntimeDefaultsPlan(ctx, globalScope)
@@
-func (s *setupCommandState) executeInstall(
+func (s *setupCommandState) executeInstall(
+	ctx context.Context,
 	cmd *cobra.Command,
 	cfg setup.InstallConfig,
 	runtimePlan runtimeDefaultsPlan,
 ) error {
@@
-	if err := s.persistRuntimeDefaults(runtimePlan); err != nil {
+	if err := s.persistRuntimeDefaults(ctx, runtimePlan); err != nil {
 		return err
 	}
 	return nil
 }

-func (s *setupCommandState) resolveRuntimeDefaultsPlan(global bool) (runtimeDefaultsPlan, error) {
+func (s *setupCommandState) resolveRuntimeDefaultsPlan(ctx context.Context, global bool) (runtimeDefaultsPlan, error) {
@@
-	configPath, err := s.runtimeDefaultsPath()
+	configPath, err := s.runtimeDefaultsPath(ctx)
@@
-	currentConfig, exists, err := s.loadConfigFile(context.Background(), configPath)
+	currentConfig, exists, err := s.loadConfigFile(ctx, configPath)
@@
-func (s *setupCommandState) runtimeDefaultsPath() (string, error) {
-	workspaceCtx, err := resolveWorkspaceContext(context.Background())
+func (s *setupCommandState) runtimeDefaultsPath(ctx context.Context) (string, error) {
+	workspaceCtx, err := resolveWorkspaceContext(ctx)
@@
-func (s *setupCommandState) persistRuntimeDefaults(plan runtimeDefaultsPlan) error {
+func (s *setupCommandState) persistRuntimeDefaults(ctx context.Context, plan runtimeDefaultsPlan) error {
@@
-	currentConfig, _, err := s.loadConfigFile(context.Background(), plan.ConfigPath)
+	currentConfig, _, err := s.loadConfigFile(ctx, plan.ConfigPath)
@@
-	if err := s.writeConfig(context.Background(), plan.ConfigPath, currentConfig); err != nil {
+	if err := s.writeConfig(ctx, plan.ConfigPath, currentConfig); err != nil {
 		return err
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/setup.go` around lines 123 - 137, The install flow currently
uses context.Background() for workspace/config I/O which drops Cobra command
cancellation; update the calls so the Cobra command context is propagated:
obtain ctx := cmd.Context() in the caller path and pass it into
resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and persistRuntimeDefaults (or
add a ctx parameter to those functions) and replace context.Background() usages
inside resolveRuntimeDefaultsPlan (loadConfigFile), runtimeDefaultsPath
(resolveWorkspaceContext), and persistRuntimeDefaults (loadConfigFile and
writeConfig) to use the passed ctx instead of context.Background(); ensure
signatures for resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and
persistRuntimeDefaults (and any intermediate helpers) accept a context.Context
and thread it into loadConfigFile, resolveWorkspaceContext, and writeConfig
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/cli/setup.go`:
- Around line 420-425: preferredRuntimeModel currently always falls back to
defaultModelForIDE(model.IDECodex) when defaults.Model is unset, causing
inherited IDEs to be ignored; change it to use defaults.Ide (if
present/non-empty) to choose the IDE-specific default model. Update
preferredRuntimeModel(workspace.DefaultsConfig) to: if defaults.Model is
nil/empty then check defaults.Ide (trim and ensure non-empty) and call
defaultModelForIDE with that IDE identifier; only if defaults.Ide is
missing/empty fall back to defaultModelForIDE(model.IDECodex). Keep existing
trimming/nullable checks and retain function name preferredRuntimeModel and the
callsite behavior used by editRuntimeDefaultsSelection.

---

Outside diff comments:
In `@internal/cli/setup.go`:
- Around line 123-137: The install flow currently uses context.Background() for
workspace/config I/O which drops Cobra command cancellation; update the calls so
the Cobra command context is propagated: obtain ctx := cmd.Context() in the
caller path and pass it into resolveRuntimeDefaultsPlan, runtimeDefaultsPath,
and persistRuntimeDefaults (or add a ctx parameter to those functions) and
replace context.Background() usages inside resolveRuntimeDefaultsPlan
(loadConfigFile), runtimeDefaultsPath (resolveWorkspaceContext), and
persistRuntimeDefaults (loadConfigFile and writeConfig) to use the passed ctx
instead of context.Background(); ensure signatures for
resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and persistRuntimeDefaults (and
any intermediate helpers) accept a context.Context and thread it into
loadConfigFile, resolveWorkspaceContext, and writeConfig calls.
🪄 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: 15b483aa-5cd5-4f7f-8c6c-845239c3f6b5

📥 Commits

Reviewing files that changed from the base of the PR and between 1da6a37 and a6a94a7.

📒 Files selected for processing (7)
  • internal/cli/setup.go
  • internal/cli/setup_test.go
  • internal/core/agent/registry_specs.go
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go

Comment thread internal/cli/setup.go Outdated
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from a6a94a7 to 17fa372 Compare April 12, 2026 16:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
internal/cli/setup.go (1)

123-137: ⚠️ Potential issue | 🟠 Major

Thread cmd.Context() through the runtime-defaults resolution flow instead of using context.Background().

The code calls workspace I/O at lines 279, 388, 400, and 407 with context.Background(), which disables command cancellation and timeout handling. Pass cmd.Context() into buildInstallPlan, resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and persistRuntimeDefaults to respect cancellation signals. This aligns with the coding guideline: "Pass context.Context as the first argument to all functions crossing runtime boundaries; avoid context.Background() outside main and focused tests."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/setup.go` around lines 123 - 137, The install flow uses
context.Background() in workspace I/O calls which prevents cancellation; thread
cmd.Context() through the runtime-defaults resolution by changing
buildInstallPlan to accept a context (use cmd.Context() when calling
s.buildInstallPlan) and update all downstream functions:
resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and persistRuntimeDefaults to
take a context.Context as their first parameter; then pass the received ctx
through each call path so workspace I/O at the points currently using
context.Background() uses the propagated cmd.Context() instead.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/cli/setup.go`:
- Around line 274-286: The code calls s.loadConfigFile(configPath) (via
s.runtimeDefaultsPath()) before prompting the user in
promptRuntimeDefaultsConfig, which makes a malformed/unreadable
.compozy/config.toml abort setup even if the user would decline opt‑in; change
the flow in the runtime defaults logic (runtimeDefaultsPlan creation path) to
either (a) call promptRuntimeDefaultsConfig first and only call s.loadConfigFile
if the user opts in, or (b) treat load errors as non-fatal by skipping the
repo-defaults flow when s.loadConfigFile returns an error (returning a
default/empty currentConfig to promptRuntimeDefaultsConfig). Update code paths
referencing runtimeDefaultsPlan, s.runtimeDefaultsPath, s.loadConfigFile and
promptRuntimeDefaultsConfig accordingly so config file parsing is deferred until
after the user confirms opt-in or gracefully ignored on read failures.

---

Outside diff comments:
In `@internal/cli/setup.go`:
- Around line 123-137: The install flow uses context.Background() in workspace
I/O calls which prevents cancellation; thread cmd.Context() through the
runtime-defaults resolution by changing buildInstallPlan to accept a context
(use cmd.Context() when calling s.buildInstallPlan) and update all downstream
functions: resolveRuntimeDefaultsPlan, runtimeDefaultsPath, and
persistRuntimeDefaults to take a context.Context as their first parameter; then
pass the received ctx through each call path so workspace I/O at the points
currently using context.Background() uses the propagated cmd.Context() instead.
🪄 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: f3bfac45-2f7a-4d61-a065-d7e638febf2e

📥 Commits

Reviewing files that changed from the base of the PR and between a6a94a7 and 17fa372.

📒 Files selected for processing (7)
  • internal/cli/setup.go
  • internal/cli/setup_test.go
  • internal/core/agent/registry_specs.go
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config_test.go
  • internal/cli/setup_test.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_types.go

Comment thread internal/cli/setup.go Outdated
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from 17fa372 to a902be8 Compare April 12, 2026 16:52

@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.

🧹 Nitpick comments (1)
internal/cli/setup.go (1)

381-387: Consider threading context from the command.

This function and persistRuntimeDefaults use context.Background() rather than accepting a context parameter. While acceptable for CLI commands, threading cmd.Context() would align better with the codebase guidelines and enable cancellation if the CLI infrastructure supports it.

♻️ Optional: Thread context through the call chain
-func (s *setupCommandState) runtimeDefaultsPath() (string, error) {
-	workspaceCtx, err := resolveWorkspaceContext(context.Background())
+func (s *setupCommandState) runtimeDefaultsPath(ctx context.Context) (string, error) {
+	workspaceCtx, err := resolveWorkspaceContext(ctx)
 	if err != nil {
 		return "", err
 	}
 	return workspaceCtx.ConfigPath, nil
 }

And propagate from callers using cmd.Context().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/setup.go` around lines 381 - 387, Change runtimeDefaultsPath to
accept a context.Context parameter and use it when calling
resolveWorkspaceContext instead of context.Background(); do the same for
persistRuntimeDefaults so both functions take ctx and propagate cancellation.
Update callers to pass through the command context (cmd.Context()) into
persistRuntimeDefaults and runtimeDefaultsPath. Keep the same return values and
error handling, only replace context.Background() with the passed-in ctx and
ensure resolveWorkspaceContext is invoked with that ctx.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/cli/setup.go`:
- Around line 381-387: Change runtimeDefaultsPath to accept a context.Context
parameter and use it when calling resolveWorkspaceContext instead of
context.Background(); do the same for persistRuntimeDefaults so both functions
take ctx and propagate cancellation. Update callers to pass through the command
context (cmd.Context()) into persistRuntimeDefaults and runtimeDefaultsPath.
Keep the same return values and error handling, only replace
context.Background() with the passed-in ctx and ensure resolveWorkspaceContext
is invoked with that ctx.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c6549ab-61fe-4884-97f4-b7f0e32d1b8c

📥 Commits

Reviewing files that changed from the base of the PR and between 17fa372 and a902be8.

📒 Files selected for processing (7)
  • internal/cli/setup.go
  • internal/cli/setup_test.go
  • internal/core/agent/registry_specs.go
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
✅ Files skipped from review due to trivial changes (1)
  • internal/core/workspace/config_types.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/model/runtime_config.go
  • internal/cli/setup_test.go

@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from a902be8 to b0ce303 Compare April 12, 2026 17:15

@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

🧹 Nitpick comments (1)
internal/cli/setup.go (1)

397-410: Move this config update behind a single atomic workspace helper.

This is now a full read-modify-write of .compozy/config.toml. Because workspace.LoadConfigFile and workspace.WriteConfig are separate operations in internal/core/workspace/config.go:87-124, concurrent writers can still drop unrelated config edits. I’d rather keep the merge-and-write logic in one workspace-layer helper so atomic write/locking behavior lives in one place.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/setup.go` around lines 397 - 410, persistRuntimeDefaults
currently does a separate read (s.loadConfigFile) and write (s.writeConfig)
which can lead to lost concurrent updates; replace that read-modify-write here
with a single atomic workspace helper (e.g., add workspace.ModifyConfig or
workspace.UpdateConfig that takes the ConfigPath and a callback to mutate the
loaded config under a lock and writes it back). Move the merge logic that sets
Defaults.IDE, Defaults.Model, Defaults.ReasoningEffort into that callback and
call the new helper from persistRuntimeDefaults (keep signature of
runtimeDefaultsPlan and persistRuntimeDefaults), removing the direct calls to
s.loadConfigFile and s.writeConfig so all config mutations go through the
workspace-level atomic helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/cli/setup.go`:
- Around line 296-315: The code currently sets runtimeDefaultsPlan.Existing
based only on the file-exists flag `exists` returned by `loadConfigFile`; change
this so Existing reflects whether `[defaults]` actually exists in the loaded
config by checking `currentConfig.Defaults` (or the concrete field that
represents defaults) and only setting Existing = true when `exists` AND the
defaults section is non-nil/non-empty; update the creation of
runtimeDefaultsPlan (and any related logic that expects Existing) to use this
combined condition so creating a new `[defaults]` from a file that lacks the
defaults section is treated as a create rather than an update.

---

Nitpick comments:
In `@internal/cli/setup.go`:
- Around line 397-410: persistRuntimeDefaults currently does a separate read
(s.loadConfigFile) and write (s.writeConfig) which can lead to lost concurrent
updates; replace that read-modify-write here with a single atomic workspace
helper (e.g., add workspace.ModifyConfig or workspace.UpdateConfig that takes
the ConfigPath and a callback to mutate the loaded config under a lock and
writes it back). Move the merge logic that sets Defaults.IDE, Defaults.Model,
Defaults.ReasoningEffort into that callback and call the new helper from
persistRuntimeDefaults (keep signature of runtimeDefaultsPlan and
persistRuntimeDefaults), removing the direct calls to s.loadConfigFile and
s.writeConfig so all config mutations go through the workspace-level atomic
helper.
🪄 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: 74d24c06-1e69-4942-85dc-02ea10c121e0

📥 Commits

Reviewing files that changed from the base of the PR and between a902be8 and b0ce303.

📒 Files selected for processing (7)
  • internal/cli/setup.go
  • internal/cli/setup_test.go
  • internal/core/agent/registry_specs.go
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
✅ Files skipped from review due to trivial changes (4)
  • internal/core/model/runtime_config.go
  • internal/cli/setup_test.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_types.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/core/workspace/config_test.go

Comment thread internal/cli/setup.go
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 12, 2026
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from b0ce303 to 3fa10b0 Compare April 12, 2026 18:05

@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.

🧹 Nitpick comments (1)
internal/cli/setup.go (1)

413-415: Wrap the runtime-defaults write failure with local context.

If writeConfig fails here, the caller loses the fact that setup was persisting repo defaults, which makes a post-install failure harder to diagnose.

Proposed fix
 	currentConfig.Defaults.IDE = plan.Defaults.IDE
 	currentConfig.Defaults.Model = plan.Defaults.Model
 	currentConfig.Defaults.ReasoningEffort = plan.Defaults.ReasoningEffort
 	if err := s.writeConfig(ctx, plan.ConfigPath, currentConfig); err != nil {
-		return err
+		return fmt.Errorf("write runtime defaults config %q: %w", plan.ConfigPath, err)
 	}
 	return nil
 }

As per coding guidelines, "Prefer explicit error returns with wrapped context using fmt.Errorf("context: %w", err)".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/setup.go` around lines 413 - 415, When persisting repo defaults
using s.writeConfig(ctx, plan.ConfigPath, currentConfig) a raw error is returned
to the caller; wrap that error with local context so callers know this was a
failure writing runtime-defaults. Update the return to wrap the error using
fmt.Errorf("persisting repo defaults to %s: %w", plan.ConfigPath, err) (or
similar) where s.writeConfig is called, referencing s.writeConfig,
plan.ConfigPath and currentConfig so the failure message includes the config
path and context.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/cli/setup.go`:
- Around line 413-415: When persisting repo defaults using s.writeConfig(ctx,
plan.ConfigPath, currentConfig) a raw error is returned to the caller; wrap that
error with local context so callers know this was a failure writing
runtime-defaults. Update the return to wrap the error using
fmt.Errorf("persisting repo defaults to %s: %w", plan.ConfigPath, err) (or
similar) where s.writeConfig is called, referencing s.writeConfig,
plan.ConfigPath and currentConfig so the failure message includes the config
path and context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 022a95fe-70f8-444b-9646-cb58e90d0912

📥 Commits

Reviewing files that changed from the base of the PR and between b0ce303 and 3fa10b0.

📒 Files selected for processing (7)
  • internal/cli/setup.go
  • internal/cli/setup_test.go
  • internal/core/agent/registry_specs.go
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go
  • internal/core/workspace/config_test.go
  • internal/core/workspace/config_types.go
✅ Files skipped from review due to trivial changes (3)
  • internal/core/workspace/config_test.go
  • internal/cli/setup_test.go
  • internal/core/workspace/config_types.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/core/model/runtime_config.go
  • internal/core/workspace/config.go

@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from 3fa10b0 to 8341c23 Compare April 14, 2026 10:57

@pedronauck pedronauck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the PR. I like the direction here: integrating repo-level runtime defaults into compozy setup closes a real usability gap, and I confirmed the branch passes make verify locally on commit 8341c23.

I do not think this is ready to merge yet, because the new flow conflicts with existing workspace/setup semantics in a few important ways:

  1. Project-scope setup and workspace config can target different directories.
    resolveRuntimeDefaultsPlan() / runtimeDefaultsPath() in internal/cli/setup.go now resolve .compozy/config.toml through workspace discovery, but project-scope skill installation in internal/setup/install.go still resolves paths relative to the current working directory. If compozy setup is run from a nested directory, this PR can write config in the workspace root while installing skills under the nested directory's .agents/skills, creating split state across two locations.

  2. The runtime-defaults prompt ignores the agents selected in the same setup run.
    The install plan already knows selectedAgents, but the defaults step still falls back to Codex when there is no existing config. That means a user can install only Claude or Cursor, accept the suggested defaults, and still end up with defaults.ide = "codex". Because workspace defaults are later applied directly to command state in internal/cli/workspace_config.go, and bundled-skill preflight validates the selected IDE in internal/cli/skills_preflight.go, the next start / fix-reviews invocation can fail with missing Codex skills immediately after setup.

  3. The "optional" defaults step is not actually optional when config parsing fails.
    Before the user answers the opt-in prompt, runtimeDefaultsPath() resolves the workspace, and workspace.Resolve() already loads/parses .compozy/config.toml. A malformed or unreadable config now aborts interactive compozy setup even if the user would choose No and skip repo defaults entirely. That looks like a regression in the current setup flow.

Recommendation: I would address these before merging. The feature itself is useful and not duplicative, but the current implementation introduces scope inconsistencies and post-setup failure modes.

@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from 8341c23 to 6251f58 Compare April 18, 2026 13:58
@camopy
camopy requested a review from pedronauck April 18, 2026 14:28
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from 6251f58 to de37eca Compare April 29, 2026 11:31

@pedronauck pedronauck left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@camopy there are some conflicts to solve here

@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from de37eca to 95b1487 Compare June 14, 2026 16:48
@camopy
camopy requested a review from pedronauck June 14, 2026 16:49
@greptile-apps

greptile-apps Bot commented Jun 14, 2026 •

Copy link
Copy Markdown

Greptile Summary

Adds an optional interactive defaults step to compozy setup that lets users persist [defaults] (IDE, model, reasoning effort) to .compozy/config.toml without editing TOML by hand. The step is skipped when --yes, --all, or --global is passed, and the config write is suppressed if any skill installation fails.

  • internal/cli/setup.go: New resolveRuntimeDefaultsPlan / persistRuntimeDefaults pipeline wired into the existing setup flow; all I/O is injected, making the new code fully testable without hitting the filesystem.
  • internal/core/workspace/config.go: Adds WriteConfig and LoadConfigFile public functions; config_types.go adds omitempty to struct-level TOML tags to prevent empty sections from appearing in written files.
  • internal/cli/skills_preflight.go: Introduces preflightResolverOptions so the discovered workspace root is propagated into every bundled-skill verify/refresh call instead of being re-read from env each time.

Confidence Score: 5/5

Safe to merge — the new config-write path is properly guarded behind user opt-in and skipped entirely on install failure.

The feature is well-isolated: all filesystem I/O uses injected dependencies, the write is conditional on both user opt-in and a clean install result, and the existing config sections are preserved via a reload-before-write approach. Test coverage is thorough, including context propagation, global-mode skipping, failure-skipping, and section-preservation round-trips.

internal/core/workspace/config.go — the cfg.Validate() error path is the only place in WriteConfig that returns without wrapping.

Important Files Changed

Filename Overview
internal/cli/setup.go Core feature addition: interactive repo-level defaults step with IDE/model/reasoning selection, opt-in prompt, and conditional persistence after successful install. Well-structured with injected dependencies for all I/O operations.
internal/core/workspace/config.go Adds WriteConfig and LoadConfigFile public functions. Minor style inconsistency: cfg.Validate() error is returned bare while all other errors in the function are wrapped.
internal/core/workspace/config_types.go Adds omitempty to top-level ProjectConfig fields and RuntimeOverrides pointer fields, preventing empty TOML sections from appearing in WriteConfig output.
internal/cli/setup_test.go Comprehensive new test coverage: context propagation, config preservation, global-mode skipping, failure-skipping, and existing-defaults detection — all wired through the real resolveRuntimeDefaultsPlan rather than re-implementing its logic.
internal/core/agent/registry_specs.go Adds DefaultModel(ide string) string lookup that returns the registry-backed default model for a given IDE, falling back to empty string on unknown IDE.
internal/cli/skills_preflight.go Adds preflightResolverOptions helper that merges stored ResolverOptions with the live workspace root, ensuring CWD is always propagated to bundled-skill verify/refresh calls.
internal/core/model/runtime_config.go Extracts DefaultReasoningEffort = "medium" constant and uses it in ApplyDefaults, eliminating the hardcoded literal that setup now also references.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([compozy setup]) --> B[resolverOptions\nresolveWorkspace once]
    B --> C[loadCatalog]
    C --> D[buildInstallPlan]
    D --> E{--yes or\n--global?}
    E -- Yes --> F[runtimePlan.Enabled = false]
    E -- No --> G[promptRuntimeDefaultsOptIn]
    G -- declined --> F
    G -- accepted --> H[loadConfigFile\nget current defaults]
    H --> I[editRuntimeDefaultsSelection\nIDE to Model to ReasoningEffort]
    I --> J[runtimePlan.Enabled = true]
    F --> K[confirmPlan\nprint preview]
    J --> K
    K --> L[executeInstall\nskills + agents]
    L --> M{failures > 0?}
    M -- Yes --> N([return error\nskip config write])
    M -- No --> O[persistRuntimeDefaults\nreload then merge then WriteConfig]
    O --> P([done])
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A([compozy setup]) --> B[resolverOptions\nresolveWorkspace once]
    B --> C[loadCatalog]
    C --> D[buildInstallPlan]
    D --> E{--yes or\n--global?}
    E -- Yes --> F[runtimePlan.Enabled = false]
    E -- No --> G[promptRuntimeDefaultsOptIn]
    G -- declined --> F
    G -- accepted --> H[loadConfigFile\nget current defaults]
    H --> I[editRuntimeDefaultsSelection\nIDE to Model to ReasoningEffort]
    I --> J[runtimePlan.Enabled = true]
    F --> K[confirmPlan\nprint preview]
    J --> K
    K --> L[executeInstall\nskills + agents]
    L --> M{failures > 0?}
    M -- Yes --> N([return error\nskip config write])
    M -- No --> O[persistRuntimeDefaults\nreload then merge then WriteConfig]
    O --> P([done])
Loading

Reviews (3): Last reviewed commit: "feat: simplify repo-level default setup ..." | Re-trigger Greptile

Comment thread internal/cli/setup.go
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from 95b1487 to e259f24 Compare June 14, 2026 18:39
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 14, 2026
@camopy
camopy force-pushed the feat/repo-level-default-override-setup branch from e259f24 to 082bcad Compare June 20, 2026 14:58
@pedronauck
pedronauck merged commit b4dc542 into compozy:main Jun 30, 2026
1 check passed
@camopy
camopy deleted the feat/repo-level-default-override-setup branch July 1, 2026 20:25
This was referenced Jul 2, 2026
pedronauck added a commit that referenced this pull request Jul 3, 2026
Add human-written release notes for the next release covering the work
merged since v0.2.10:

- feat: dependency-aware parallel task execution (--parallel-tasks) (#212)
- feat: agentic recovery for failed runs (--recovery) (#212)
- feat: COMPOZY_HOME for isolated per-project daemons (#216)
- feat: repo-level default overrides in compozy setup (#90)
- fix: safer worktree management for parallel runs (#217, #223)
- fix: ACP runs consistently apply the selected model (#215)
- fix: run TUI elapsed timer restored across all terminal outcomes (#221)

Co-Authored-By: Claude Fable 5 <[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.

Repo-level default override setup

2 participants