feat(vscode): add model assignment foundation#731
Conversation
|
Maintainer label request: please apply exactly one type label, \ ype:feature\, and \size:exception\. The size exception is intentional: PR2's persistence + resolver + render + warning propagation must be reviewed as one cohesive safety contract, and the PR body documents the rationale. |
763bbf0 to
3fe7acd
Compare
📝 WalkthroughWalkthroughActivates VS Code Copilot native sub-agent support: the VS Code adapter now reports sub-agent capability and targets ChangesVS Code Copilot SDD Native Sub-Agent Support
Sequence Diagram(s)sequenceDiagram
actor User
participant RunSync
participant loadPersistedAssignments
participant sdd.Inject
participant renderVSCodeAgentModelAssignment
participant resolveVSCodeModelAssignment
participant loadVSCodeModelCache
participant SyncResult
rect rgba(100, 149, 237, 0.5)
note over RunSync,loadPersistedAssignments: Restore phase
RunSync->>loadPersistedAssignments: read state.json VSCodeModelAssignments
loadPersistedAssignments-->>RunSync: selection.VSCodeModelAssignments populated
end
rect rgba(60, 179, 113, 0.5)
note over RunSync,sdd.Inject: Injection phase
RunSync->>sdd.Inject: InjectOptions{VSCodeModelAssignments, VSCodeModelCachePath}
loop for each .agent.md file
sdd.Inject->>renderVSCodeAgentModelAssignment: filename + assignments
renderVSCodeAgentModelAssignment->>resolveVSCodeModelAssignment: lookup key
resolveVSCodeModelAssignment->>loadVSCodeModelCache: read models.json + variants.json
loadVSCodeModelCache-->>resolveVSCodeModelAssignment: catalog or cache-miss warning
resolveVSCodeModelAssignment-->>renderVSCodeAgentModelAssignment: label or warning
renderVSCodeAgentModelAssignment-->>sdd.Inject: rewritten content + warnings
end
sdd.Inject-->>RunSync: InjectionResult{Files, Warnings}
end
rect rgba(255, 165, 0, 0.5)
note over RunSync,SyncResult: Result phase
RunSync->>SyncResult: dedupStrings(warnings) -> Warnings
SyncResult-->>User: RenderSyncReport with Warnings section
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
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/app/app.go`:
- Around line 749-751: The condition in the VSCodeModelAssignments block at the
if statement is checking if the length is greater than zero, which prevents
explicit clearing when a non-nil empty map is passed. Change the condition from
checking len(selection.VSCodeModelAssignments) > 0 to instead checking if
selection.VSCodeModelAssignments is not nil. This will allow the
modelAssignmentsToState function to process empty maps that represent an
explicit clear intent, while still allowing the caller to pass nil if they want
to skip processing assignments entirely.
In `@internal/cli/run.go`:
- Around line 183-184: The mergeExplicitAgentInstallState function is not
copying the VSCodeModelAssignments field when merging state, causing newly
selected VS Code assignments to be lost when running install with an existing
state file. Add VSCodeModelAssignments to the field assignments in the
mergeExplicitAgentInstallState function to ensure the value set by RunInstall is
preserved during the state merge operation.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 415afe5b-2b1d-41d7-883e-cb87ffe57903
📒 Files selected for processing (30)
internal/agents/vscode/adapter.gointernal/agents/vscode/adapter_test.gointernal/app/app.gointernal/app/app_test.gointernal/assets/assets.gointernal/assets/assets_test.gointernal/assets/vscode/agents/sdd-apply.agent.mdinternal/assets/vscode/agents/sdd-archive.agent.mdinternal/assets/vscode/agents/sdd-design.agent.mdinternal/assets/vscode/agents/sdd-explore.agent.mdinternal/assets/vscode/agents/sdd-init.agent.mdinternal/assets/vscode/agents/sdd-onboard.agent.mdinternal/assets/vscode/agents/sdd-orchestrator.agent.mdinternal/assets/vscode/agents/sdd-propose.agent.mdinternal/assets/vscode/agents/sdd-spec.agent.mdinternal/assets/vscode/agents/sdd-tasks.agent.mdinternal/assets/vscode/agents/sdd-verify.agent.mdinternal/cli/install_test.gointernal/cli/run.gointernal/cli/sync.gointernal/cli/sync_test.gointernal/components/sdd/inject.gointernal/components/sdd/inject_test.gointernal/components/sdd/vscode_models.gointernal/components/sdd/vscode_models_test.gointernal/components/uninstall/service_test.gointernal/model/selection.gointernal/model/selection_test.gointernal/state/state.gointernal/state/state_test.go
| if len(selection.VSCodeModelAssignments) > 0 { | ||
| current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments) | ||
| } |
There was a problem hiding this comment.
Honor explicit clear semantics for VSCodeModelAssignments.
persistAssignments only writes VS Code assignments when the map is non-empty. If callers pass a non-nil empty map to clear assignments, stale persisted values remain and get rehydrated on later syncs.
Suggested fix
- if len(selection.VSCodeModelAssignments) > 0 {
- current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments)
- }
+ if selection.VSCodeModelAssignments != nil {
+ if len(selection.VSCodeModelAssignments) > 0 {
+ current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments)
+ } else {
+ current.VSCodeModelAssignments = nil
+ }
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if len(selection.VSCodeModelAssignments) > 0 { | |
| current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments) | |
| } | |
| if selection.VSCodeModelAssignments != nil { | |
| if len(selection.VSCodeModelAssignments) > 0 { | |
| current.VSCodeModelAssignments = modelAssignmentsToState(selection.VSCodeModelAssignments) | |
| } else { | |
| current.VSCodeModelAssignments = 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/app/app.go` around lines 749 - 751, The condition in the
VSCodeModelAssignments block at the if statement is checking if the length is
greater than zero, which prevents explicit clearing when a non-nil empty map is
passed. Change the condition from checking len(selection.VSCodeModelAssignments)
> 0 to instead checking if selection.VSCodeModelAssignments is not nil. This
will allow the modelAssignmentsToState function to process empty maps that
represent an explicit clear intent, while still allowing the caller to pass nil
if they want to skip processing assignments entirely.
| VSCodeModelAssignments: modelAssignmentsToState(input.Selection.VSCodeModelAssignments), | ||
| Persona: string(input.Selection.Persona), |
There was a problem hiding this comment.
Persist VS Code assignments in explicit-agent state merges.
RunInstall writes VSCodeModelAssignments into newState, but mergeExplicitAgentInstallState never copies that field. On install --agent ... with an existing state file, newly selected VS Code assignments can be lost.
Suggested fix
func mergeExplicitAgentInstallState(homeDir string, newState state.InstallState, agentIDs []string) (state.InstallState, bool) {
@@
if newState.ModelAssignments != nil {
merged.ModelAssignments = newState.ModelAssignments
}
+ if newState.VSCodeModelAssignments != nil {
+ merged.VSCodeModelAssignments = newState.VSCodeModelAssignments
+ }
if newState.ClaudeModelAssignments != nil {
merged.ClaudeModelAssignments = newState.ClaudeModelAssignments
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| VSCodeModelAssignments: modelAssignmentsToState(input.Selection.VSCodeModelAssignments), | |
| Persona: string(input.Selection.Persona), | |
| if newState.ModelAssignments != nil { | |
| merged.ModelAssignments = newState.ModelAssignments | |
| } | |
| if newState.VSCodeModelAssignments != nil { | |
| merged.VSCodeModelAssignments = newState.VSCodeModelAssignments | |
| } | |
| if newState.ClaudeModelAssignments != nil { | |
| merged.ClaudeModelAssignments = newState.ClaudeModelAssignments | |
| } |
🤖 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/run.go` around lines 183 - 184, The
mergeExplicitAgentInstallState function is not copying the
VSCodeModelAssignments field when merging state, causing newly selected VS Code
assignments to be lost when running install with an existing state file. Add
VSCodeModelAssignments to the field assignments in the
mergeExplicitAgentInstallState function to ensure the value set by RunInstall is
preserved during the state merge operation.
🔗 Linked Issue
Closes #504
Related context: #677
Depends on: #708
🏷️ PR Type
What kind of change does this PR introduce?
type:bug— Bug fix (non-breaking change that fixes an issue)type:feature— New feature (non-breaking change that adds functionality)type:docs— Documentation onlytype:refactor— Code refactoring (no functional changes)type:chore— Build, CI, or tooling changestype:breaking-change— Breaking change (fix or feature that changes existing behavior)🔗 Chain Context
PR 2 of the VS Code Copilot SDD subagents stack.
This PR depends on #708. Because #708 is still open and its branch exists only in the contributor fork, this PR targets
main; once #708 lands, this PR can be rebased/updated and the GitHub diff will collapse to the PR2-only commit.Focused review diff while #708 is open:
Snakeblack/gentle-ai@feat/vscode-copilot-subagents-pr1...feat/vscode-copilot-subagents-pr2
📝 Summary
VSCodeModelAssignmentsseparate from OpenCode, Claude, and Kiro model assignment maps.github-copilotmodel cache data and safely renders optionalmodel:frontmatter for native.agent.mdfiles.📂 Changes
internal/model,internal/stateVSCodeModelAssignmentsto selections, sync overrides, and persisted install stateinternal/app,internal/cliinternal/components/sdd/vscode_models.go.agent.mdfrontmatter renderinginternal/components/sdd/inject.go🧠 Size Exception Rationale
This PR requests
size:exception.The change is larger than the 400-line budget because the behavior is intentionally cohesive: persistence, resolver validation,
.agent.mdrendering, warning propagation, and tests must be reviewed together to understand the safety contract. Splitting these pieces would make the implementation harder to reason about and would hide the important invariant: unresolved assignments must stay persisted but must not render unsafemodel:frontmatter.To reduce reviewer load, the new resolver files include concise behavior comments explaining why each helper exists and how failures degrade safely.
🧪 Test Plan
Focused PR2 tests
Package smoke
go test ./internal/cliStatic checks
Full suite in CI-like Linux runner
env -i HOME=/tmp/opencode/home \ PATH=/tmp/opencode/go/bin:/usr/bin:/bin \ GOCACHE=/tmp/opencode/gocache \ GOMODCACHE=/tmp/opencode/gomodcache \ /tmp/opencode/go/bin/go test ./...Results:
go test ./internal/clipassesgo vet ./...passesgit diff --checkpassesgo test ./...passes in clean WSL Ubuntu LF clone with Go 1.25.10cd e2e && ./docker-test.sh)Note: local Windows
go test ./...still has baseline CRLF/env-sensitive failures unrelated to this PR. The full-suite gate was validated in a clean Linux/LF runner instead.🤖 Automated Checks
size:exception; behavior is cohesive and documentedCloses #504status:approvedstatus:approvedtype:*Labeltype:featurerequested (maintainer label may be needed)go test ./...✅ Contributor Checklist
status:approvedsize:exceptionwith rationale documented — maintainer label needed (size:exception)type:*label to this PR — maintainer label may be needed (type:feature)go test ./...) in Linux/LF runnercd e2e && ./docker-test.sh)Co-Authored-Bytrailers💬 Notes for Reviewers
This is backend/model-assignment foundation only. It deliberately does not include the VS Code install hardening or the TUI/docs updates; those remain split into #738 and #740.
Key safety behavior to review:
.agent.mdfiles are ignored by the runtime assignment allowlist;model:instead of failing sync;Summary by CodeRabbit
New Features
Improvements