Repository navigation
fix: resolve inherited cross-runtime models against runtime defaults - #259
Conversation
An ACP session inherited its model from workspace config, a task rule, or an
agent default without ever re-validating it against the runtime it landed on.
`compozy exec --agent <id> --ide codex` in a Claude-defaulted workspace sent
"opus" to Codex and died at session setup:
Error: create ACP session: set_model: model "opus" is not available
`RuntimeConfig.ApplyDefaults` defaults IDE but never touches Model, and
`applyRuntimePrecedence` only fills gaps from agent defaults, so a foreign
model survived all the way to `session/set_session_config`. Nested `run_agent`
calls hit the same wall through `buildChildRuntime`.
Resolve the model against the runtime's own advertised list instead of a static
per-IDE table: when the runtime does not advertise an inherited model, fall back
to that runtime's current default and warn. A model the caller pinned with
--model stays a hard error, because running a model other than the one requested
is worse than failing. Fixing this at session setup covers direct exec and
nested run_agent alike, since both configure the session through the same path.
Also correct the cy-idea-factory council contract. Its failure text claimed
run_agent was unavailable on Codex and told users to run `compozy setup`, which
could never help. Codex does accept host-injected stdio MCP servers; a missing
run_agent means the session was not created by Compozy. The skill now dispatches
through run_agent, falls back to `compozy exec --agent <id>`, and only points at
setup when `compozy agents list` shows advisors are genuinely absent.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
The run_agent path inherits the parent session's whole runtime, including reasoning effort, through NestedBaseRuntime. The `compozy exec --agent` fallback is a fresh CLI invocation, so it reads workspace defaults instead. The contract told it to pass --ide but said nothing about reasoning effort, so advisors could silently run at a different effort than the session that dispatched them. State the requirement and show it in the example. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Review of the fallback surfaced two gaps. The fallback substituted a model with no durable trace. Its only signal was a warning, and `exec` silences operational logs without --verbose, while SessionStartedPayload carried no model at all. A run could execute on a model nobody chose and leave nothing in the artifacts to show it. configureSession now reports the model the runtime accepted, sessions record it in their identity, and session.started carries it. This also closes a plainer gap: the resolved model was never observable, even on the happy path. The wiring that feeds the decision had no test. ModelExplicit is hand-copied through a 40-field struct literal into ClientConfig, and every existing test set ClientConfig directly, so dropping either assignment would silently downgrade an explicitly pinned model with a green suite. Both hops are now asserted; mutating either one fails a test. Also note on ClientConfig.ModelExplicit that it governs SessionRequest.Model too, which is safe only while both carry the same value. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Two things a reader cannot infer from the code. RuntimeForTask gives type and id task rules authority over --model, while ModelExplicit only distinguishes the flag from everything else. Such a rule is therefore correctable here even though it outranks the flag upstream. Encoding that faithfully would mean giving the flag three states and touching the precedence other resolution paths read, so record the limit instead. The current-value guard exists because a value the runtime omits from its own option list is no safer than the request. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
`compozy setup` installs extension skills into <cwd>/.agents/skills in project scope (internal/setup/install.go canonicalSkillsRoot), so the tracked copy at .agents/skills/cy-idea-factory is a build output that happens to be committed. Every project-scope setup rewrote files nobody edited, and the copy had already drifted from extensions/cy-idea-factory/skills/cy-idea-factory by a stray blank line. The copy predates the extension: compozy#35 created it when the skill was a plain repo skill, and compozy#93 migrated the source without removing the old path. Untracking rather than deleting keeps existing installs working; the directory stays on disk and link-skills.sh skips it when absent, so a fresh clone simply has no local link until the extension is installed. This removes the duplicate the surrounding commits had to edit in tandem. Issue #23 covers the wider question of separating tracked skill sources from install destinations under .agents/skills. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
…llers Adding Model to the runtime SessionIdentity changed the agent.post_session_create payload without updating its SDK mirrors, so TestPublicHookAndHostTypesStayAlignedWithRuntime failed on field drift and typed extensions could not read the accepted model. Both SDKs now carry the optional field. Two direct ClientConfig callers pass a user-chosen model but left ModelExplicit false, so the fallback would substitute it. That is worst in TestLiveCodexModelAvailability, whose whole purpose is to prove a requested model is available: it would have passed against a different one. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
RuntimeForTask lets type and id rules override --model, unlike complexity rules, which yield to it. Those pins nonetheless reached the ACP client as inherited values, because ModelExplicit only reflected the run-wide flag. A `--task-runtime id=task_01,model=...` selection the runtime did not advertise was therefore replaced by the runtime default instead of failing. Marking the pin at the point that already grants it authority keeps the two consistent: a rule that outranks the flag is at least as deliberate as one. Complexity rules stay inherited, matching their weaker precedence. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughThe PR propagates explicit model provenance through runtime preparation and ACP sessions, falls back unavailable inherited models to runtime defaults, records accepted models in session metadata and events, adds mutable-hook status reporting, and ignores a generated skill destination. ChangesRuntime model selection
Skill installation artifacts
Estimated code review effort: 3 (Moderate) | ~30 minutes Sequence Diagram(s)sequenceDiagram
participant RuntimeConfig
participant HookDispatcher
participant ACPClient
participant AgentSession
participant SessionEvent
RuntimeConfig->>HookDispatcher: Dispatch task runtime hook
HookDispatcher->>RuntimeConfig: Return updated runtime and applied status
RuntimeConfig->>ACPClient: Pass model and explicitness
ACPClient->>AgentSession: Configure session model
AgentSession->>AgentSession: Resolve inherited fallback
AgentSession->>SessionEvent: Emit accepted model
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
| Filename | Overview |
|---|---|
| internal/core/model/task_runtime.go | Preserves explicit model and IDE authority while applying task runtime rules and hook-selected fields. |
| internal/core/plan/prepare.go | Propagates per-job model explicitness and uses hook mutation metadata to distinguish selected fields. |
| internal/core/model/hooks.go | Adds mutation-status dispatch so hooks can report which runtime fields they selected. |
| internal/core/run/internal/acpshared/command_io.go | Passes combined run/job model explicitness into ACP clients and emits the accepted session model. |
| internal/core/agent/client.go | Captures the model resolved during session configuration in session identity. |
| internal/core/agent/session_config.go | Resolves unavailable inherited models against advertised runtime defaults while rejecting incompatible explicit pins. |
| extensions/cy-idea-factory/skills/cy-idea-factory/references/council.md | Documents host-tool-first advisor dispatch with a runtime-preserving CLI fallback. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Runtime defaults and task rules] --> B[Pre-resolution hook]
B --> C[Resolved job model and explicitness]
C --> D[ACP client]
D --> E[Runtime-advertised model options]
E --> F{Requested model available?}
F -->|Yes| G[Configure requested model]
F -->|No, inherited| H[Use runtime default]
F -->|No, explicit| I[Return incompatibility error]
G --> J[Session identity and started event]
H --> J
Reviews (5): Last reviewed commit: "fix: preserve hook authority per runtime..." | Re-trigger Greptile
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39e5d0146f
ℹ️ 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".
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/core/agent/client_test.go`:
- Around line 706-757: Convert
TestClientCreateSessionFallsBackWhenInheritedModelIsFromAnotherRuntime into a
table-driven test with t.Run subtests named using the “Should ...” convention.
Add cases covering both Codex and Cursor inherited cross-runtime model fallback,
parameterizing runtime-specific configuration and expectations while preserving
the existing session identity and artifact recovery assertions.
In `@internal/core/agent/session_config.go`:
- Around line 157-169: Validate the fallback model derived from CurrentValue
before returning it from the surrounding session configuration logic. Reuse the
existing model-availability or selectability check, and only return current,
true when it is valid; otherwise return "", false so the original
unavailable-model error is preserved. Keep the existing empty and
requested-model comparisons and warning behavior intact.
In `@internal/core/model/model_test.go`:
- Around line 609-620: Extend the relevant subtest around RuntimeForTask to
resolve a target matching an ID rule, then assert that both
ExplicitRuntime.Model and ExplicitRuntime.IDE are true, alongside the existing
type-rule assertions. Keep the test focused on explicitness state management and
use the existing matching ID-rule configuration.
🪄 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: 684f5b9b-6242-42e5-9d69-ffacecf8840b
⛔ Files ignored due to path filters (10)
.agents/skills/cy-idea-factory/SKILL.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/adr-template.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/business-analyst.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/council.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/idea-template.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/product-strategist.mdis excluded by!**/*.md,!.agents/**.agents/skills/cy-idea-factory/references/question-protocol.mdis excluded by!**/*.md,!.agents/**docs/events.mdis excluded by!**/*.mdextensions/cy-idea-factory/skills/cy-idea-factory/SKILL.mdis excluded by!**/*.mdextensions/cy-idea-factory/skills/cy-idea-factory/references/council.mdis excluded by!**/*.md
📒 Files selected for processing (16)
.gitignoreinternal/core/agent/client.gointernal/core/agent/client_test.gointernal/core/agent/codex_live_test.gointernal/core/agent/session.gointernal/core/agent/session_config.gointernal/core/model/model_test.gointernal/core/model/task_runtime.gointernal/core/run/internal/acpshared/command_io.gointernal/core/run/internal/acpshared/command_io_test.gointernal/core/run/internal/runshared/config.gointernal/core/run/internal/runshared/config_test.gopkg/compozy/events/kinds/session.goscripts/inspect-acp-toolcalls.gosdk/extension-sdk-ts/src/types.tssdk/extension/types.go
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
internal/core/plan/prepare_test.go (1)
551-559: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap this scenario in a
t.Run("Should...")subtest.The added assertions are meaningful, but this test case currently runs directly in the parent test body and does not follow the required subtest pattern. Move the runtime-override setup and assertions into a descriptive
t.Run("Should preserve explicit model provenance", ...)block.🤖 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/plan/prepare_test.go` around lines 551 - 559, Wrap the runtime-override setup and all related assertions in the parent test with a descriptive t.Run("Should preserve explicit model provenance", ...) subtest. Keep the existing jobs[0] and jobs[1] provenance and runtime validations unchanged inside that subtest.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/core/run/internal/runshared/config_test.go`:
- Around line 56-69: Update TestNewJobsCarriesPerJobExplicitModelFlag to wrap
the existing assertions in a t.Run subtest named “Should preserve the per-job
explicit model flag”. Add a corresponding “Should…” subtest covering
ModelExplicit=false to verify NewJobs preserves both flag values.
---
Nitpick comments:
In `@internal/core/plan/prepare_test.go`:
- Around line 551-559: Wrap the runtime-override setup and all related
assertions in the parent test with a descriptive t.Run("Should preserve explicit
model provenance", ...) subtest. Keep the existing jobs[0] and jobs[1]
provenance and runtime validations unchanged inside that subtest.
🪄 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: 821c02ea-9fc6-4aed-bd99-bdf8daec4023
📒 Files selected for processing (13)
internal/core/agent/client_test.gointernal/core/agent/session_config.gointernal/core/model/model_test.gointernal/core/model/preparation.gointernal/core/plan/prepare.gointernal/core/plan/prepare_test.gointernal/core/run/executor/hooks.gointernal/core/run/executor/runner.gointernal/core/run/internal/acpshared/command_io.gointernal/core/run/internal/acpshared/command_io_test.gointernal/core/run/internal/runshared/config.gointernal/core/run/internal/runshared/config_test.gosdk/extension/types.go
🚧 Files skipped from review as they are similar to previous changes (5)
- internal/core/run/internal/runshared/config.go
- internal/core/run/internal/acpshared/command_io.go
- internal/core/agent/session_config.go
- internal/core/model/model_test.go
- internal/core/agent/client_test.go
|
Addressed the remaining top-level CodeRabbit nitpick in 95ae239: the per-task runtime override scenario now runs under a Should preserve explicit model provenance subtest, and the NewJobs mapping covers both explicit and inherited values. The hook-authority regression is fixed in the same commit. Full make verify passes locally: 4,921 Go tests, strict lint with 0 issues, all frontend checks and builds, extension verification, and 5 E2E tests. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/core/plan/prepare_test.go`:
- Around line 1540-1542: Wrap the test case in
TestPreparePlanPreResolveTaskRuntimeMutationUpdatesPreparedJob with a named
t.Run subtest using the required “Should...” naming pattern, including the
existing ModelExplicit assertion and related setup. Preserve the test behavior
while ensuring all assertions execute within that subtest.
🪄 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: 095ac82d-06bb-4bc9-97d1-8495c4238539
📒 Files selected for processing (3)
internal/core/model/task_runtime.gointernal/core/plan/prepare_test.gointernal/core/run/internal/runshared/config_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- internal/core/run/internal/runshared/config_test.go
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/core/extension/dispatcher.go`:
- Around line 67-69: Remove the context.Background fallback from
DispatchMutableWithStatus and DispatchObserver so nil contexts are rejected
before hook invocation, preserving the caller’s cancellation and deadline. If
internal safety requires a guard, move it into invokeHook rather than replacing
the caller context in the dispatch methods.
In `@internal/core/model/task_runtime.go`:
- Around line 220-235: Update ApplyExplicitTaskRuntime to use per-field hook
patch provenance instead of treating every non-empty cfg field as explicit. Mark
only the IDE, Model, and ReasoningEffort fields actually selected or changed by
the hook, preserving inherited values and fallback behavior for untouched fields
while retaining ApplyTaskRuntime’s existing application flow.
🪄 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: d0af0782-0591-4e9d-b73b-61bf84da367a
📒 Files selected for processing (7)
internal/core/extension/dispatcher.gointernal/core/extension/dispatcher_test.gointernal/core/extension/manager.gointernal/core/model/hooks.gointernal/core/model/task_runtime.gointernal/core/plan/prepare.gointernal/core/plan/prepare_test.go
Summary
Fixes cross-runtime session setup for runtimes that advertise their model choices (currently Codex and Cursor), and makes
cy-idea-factorycouncil dispatch recover when invoked outside a Compozy-created session.Fixes #260.
Root cause
Runtime defaults and agent/task configuration can carry a model from one provider into a different IDE. Before this change, Compozy sent that inherited value directly to
session/set_session_config. For example, a workspace defaulting to Claudeopusand then running with--ide codexfails because Codex does not advertiseopus.The original report also assumed Codex could not receive
run_agent. Direct ACP probing showed that assumption was incorrect: Compozy can inject its reserved stdio MCP server into Codex sessions. The actual portability gap is sessions launched outside Compozy, where the skill has no hostrun_agenttool.What changed
Resolve inherited models from runtime-advertised choices
session/new.--modeland higher-authority type/id task rules) as hard errors; Compozy never silently replaces a deliberate pin.Make model substitution auditable
SessionIdentity.session.startedevents.Make council dispatch portable
run_agenttool.compozy exec --agent <id>when the current session was not created by Compozy.compozy setuponly whencompozy agents listshows that advisor definitions are missing.Remove the generated skill mirror
.agents/skills/cy-idea-factorywas both tracked and used as the project-scope install destination forcompozy setup. That duplicate could drift from the canonical extension source atextensions/cy-idea-factory/skills/cy-idea-factory, dirtying worktrees and sometimes loading stale content.A broader source-versus-install-destination redesign remains separate; background: fork issue 23.
Scope and limitations
This model fallback only applies to runtimes that advertise model options after session creation: Codex and Cursor. Claude and Droid select models during process bootstrap, before advertised session configuration exists. Supporting foreign inherited models there requires model provenance in precedence resolution; tracked separately in fork issue 24.
Sessions not launched by Compozy still cannot receive injected
run_agent; the skill's CLI fallback handles that case, but starts one process per advisor and does not inheritrun_agentdepth/cycle guards.Validation
make verify0 issues)Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores