Repository navigation
feat: reliability improvements - agent settings, unified sidebar, test coverage - #11
Merged
Merged
Conversation
investigation FailPermanently now adds the needs-investigation label and records the failure reason in notes, but no longer closes the bead. This allows humans to investigate failed tasks and manually retry them when needed, rather than silently closing them.
beads
Add a built-in default rule that automatically excludes tasks with
needs-investigation, needs-input, or other needs-* labels from canopy
runs. These tasks require manual attention and should not be picked up
by automated orchestration.
Implementation:
- Define DefaultCustomRules in pkg/config/config.go with the
exclude-needs-labels rule using wildcard pattern 'needs-*' in labels
- Update NewEngineWithDefaults to handle shadowing: config rules can
disable default rules by adding a rule with the same name and
enabled=false
- Update all rules engine creation sites to use NewEngineWithDefaults
with the default rules
Users can disable the default rule by adding to their config:
[rules]
custom = [{ name = "exclude-needs-labels", enabled = false }]
Add [agents] section to .canopy/config.toml schema for model configuration. - AgentSettings struct with DefaultModel and per-type settings - AgentTypeSettings struct with Model, Enabled, and Timeout fields - DefaultAgentSettings() for sensible defaults (empty model = CLI default) - Validate() method for AgentSettings (validates timeout formats) - Tests for loading, saving, and validating agent configuration
Add Model field to agent.Config and pass it to the Claude CLI via --model flag in both Execute() and ExecuteResume() methods. The model is propagated through the configuration chain: - orchestrator.Config.Model -> executor, mergecoordinator - mergecoordinator.Config.Model -> resolver - mergequeue.Processor.model -> repairagent This allows callers to specify which Claude model to use for all agents including workers, resolvers, and repair agents.
executors Wire up config loading to pass agent-specific model settings to each executor type (worker, resolver, repair). Changes: - Add GetWorkerModel(), GetResolverModel(), GetRepairModel() helper methods to AgentSettings for resolving effective model per agent type - Update orchestrator.Config to include AgentSettings field - Update orchestrator.New() to determine per-agent-type models from AgentSettings (CLI --model flag still takes precedence) - Split mergecoordinator.Config.Model into ResolverModel and RepairModel - Pass repair model to processor in mergecoordinator.New() Model resolution priority: 1. CLI --model flag (highest) 2. Per-agent-type model from config.toml (e.g., agents.worker.model) 3. DefaultModel from config.toml (agents.default_model) 4. Empty string (use Claude CLI default)
…2dkq) Add API endpoints for managing agent settings at runtime with persistence: - GET /api/repos/:repo_id/config/agents - Get current agent settings - POST /api/repos/:repo_id/config/agents - Update agent settings (partial) - POST /api/repos/:repo_id/config/agents/persist - Persist to disk Implementation follows the rules engine pattern: - Track runtime vs persisted state in RepoAPI implementations - Broadcast agent_config:changed events via WebSocket - Support partial updates for default_model and per-agent-type settings - Validate settings before applying changes Files: - pkg/daemon/agent_config_handlers.go - NEW: API handlers - pkg/daemon/config_handlers.go - Route agent config requests - pkg/daemon/server.go - Add AgentConfigHandler - pkg/events/events.go - Add EventAgentConfigChanged - pkg/orchestrator/api.go - Add RepoAPI agent config methods
…0sik) vs conditions) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frontend used `conditions: string[]` (plural, array) while Go backend uses `condition: string` (singular, string). This mismatch caused rules added via the web UI to silently fail—the Go JSON unmarshaler never found the `condition` key, so validation rejected the empty string. Changed all TypeScript interfaces and components to use `condition: string` matching the Go `CustomRule` JSON tag.
AgentChainTimeline Both prior-attempt and current agents perform the same implementor role. Using 'Worker' for retries was inconsistent and confusing.
overlap detection Adds a function that returns the list of files modified between two commits using `git diff --name-only`. This enables the stale overlay optimization to check if concurrent changes overlap with agent changes before spawning a resolver.
file overlap When HEAD has moved ahead of an overlay's base commit, check if the agent's changed files overlap with concurrently merged files before spawning a resolver. If there's no overlap, proceed with direct git am (fast path), avoiding the expensive resolver agent spawn. Falls back to resolver when overlap exists or when the file check fails.
…2wq) viewer The dashboard diff viewer showed 'No diff available' because patch data wasn't extracted or propagated through the pipeline. Three fixes: 1. Use GetCommitInfoFromDirWithOptions with IncludePatch: true in sequential.go to actually extract diffs during merge 2. Add Patch and Truncated fields to CommitEvent struct and populate them in sendMergedCommits() with MaxPatchSize truncation 3. Include patch and truncated in the event payload in createCommitCallback()
…3nag) store The rules:changed event handler was logging events but not updating the UI state. Now handles added/updated/deleted/persisted actions by calling the appropriate store mutations, and refetches the full rules list from the API for bulk actions (config_updated, persisted_all, reordered).
…tes on daemon restart Extend MarkOrphanedAgentsFailed() to detect agents in non-terminal merge-related lifecycle states (merging, queued_for_merge, validating, repairing, resolving) and mark them as failed during state restoration.
Add AgentConfigPanel modal for viewing and editing per-agent-type model settings (Worker, Resolver, Repair). Integrates with existing backend API endpoints (GET/POST /api/repos/:id/config/agents, POST .../persist). - New AgentConfigPanel.tsx with default model selector, per-type model overrides, enabled toggles, and timeout fields - New useAgentConfig.ts hook for API integration and WebSocket reactivity - Agent config types and API functions added to client.ts - WebSocket handler for agent_config:changed events - Bot icon button in dashboard header to open the panel
…mlvb) process handles When a daemon restarts, agents restored from the database may appear in RuntimeState but have no corresponding process in the orchestrator. Previously, `canopy kill` would fail with 'not found' for these agents, leaving them permanently stuck. Now when the orchestrator returns 'not found' but the agent exists in RuntimeState with a non-terminal status, HandleKillAgent falls back to marking the agent as failed directly in both RuntimeState and persistence, then returns success.
ImplementorSystemPrompt Align system prompt naming with the 'Implementor' terminology used elsewhere in the codebase.
Add 'exclude-epics' rule to DefaultCustomRules that denies tasks with type == epic. Epics define acceptance criteria and should not be directly implemented; agents should work their child tasks instead.
compliance AgentTypeSettings had toml tags but no json tags, causing Go's default capitalized key names (Model, Enabled, Timeout) in API responses. Added json:"model", json:"enabled", json:"timeout" tags and updated the TypeScript interface to use snake_case field names.
…-emub) Runtime agent settings changes via the dashboard API were validated and stored but never propagated to the running executor, resolver, or repair agent creation paths. Changes appeared to save but had no effect until restart. - Add SetModel/SetTimeout methods to agent.Executor - Add SetModel to resolver.Resolver - Add SetResolverModel to mergecoordinator.MergeCoordinator - Add GetExecutor to scheduler.Scheduler - Add UpdateAgentSettings to Orchestrator that propagates to all components - Call UpdateAgentSettings from repoAPI.UpdateAgentConfig for live updates - Plumb worker timeout from AgentTypeSettings into executor at construction
AgentTypeSettings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses roborev finding 668 (testing gap): verifies that AgentTypeSettings marshals to snake_case JSON keys (model, enabled, timeout) and not PascalCase, preventing accidental API contract breaks. The backward-compatibility concern (Medium) is not applicable—this is an internal API between the Go backend and React dashboard, both updated in the same commit (cfe396b).
agent settings - Add sync.RWMutex to Executor to protect Model/Timeout from concurrent read/write between Execute() and SetModel()/SetTimeout() - Snapshot model and timeout under lock in Execute/ExecuteResume - Guard UpdateAgentSettings against empty model strings from Get*Model() which can return "" when no default is configured - Allow timeout clearing: SetTimeout(0) resets to DefaultTimeout - Log warnings for invalid duration strings instead of silently ignoring - Add tests for SetModel/SetTimeout including race detector coverage Addresses roborev findings from job 670 (canopy-a74a601f).
partial updates) Cover JSON serialization contract (snake_case keys in request/response), partial update behavior (nil vs false, single-field updates, empty body), routing dispatch, request/response key roundtrip consistency, writeJSON helper, and nil eventBus safety.
orchestrator flows Add 14 tests covering agent settings (model/timeout) propagation through worker, resolver, and repair agent flows in pkg/orchestrator: - Worker/resolver/repair model propagation via UpdateAgentSettings - CLI model override takes precedence over config-based settings - Worker timeout parsing and propagation - DefaultModel fallback when per-agent model is empty - Nil settings is a safe no-op - Invalid timeout is ignored without crash - Initial settings from config are correctly plumbed at construction - RepoAPI.UpdateAgentConfig propagates to executor and merge coordinator - RepoAPI rejects invalid timeout values - PersistAgentConfig roundtrips through config.toml Supporting changes: - Export GetModelAndTimeout on agent.Executor (was unexported) - Add GetModel to resolver.Resolver and mergequeue.Processor - Add GetResolverModel/GetRepairModel to MergeCoordinator
- Add .codex/ to .gitignore and remove tracked .codex/ artifacts (session logs, system skills, temp binaries) - Allow empty model to clear previously set model in UpdateAgentSettings, so removing agents.*.model from config returns to CLI default - Reject negative duration strings in UpdateAgentSettings with warning - Add doc comment to SetTimeout noting negative durations are ignored - Add tests for empty-model clearing and negative timeout edge cases
- Guard against nil snapshot in mockRepoAPIForConfig.UpdateAgentConfig - Remove unused testAgentConfigHandler/newTestHandler scaffolding - Add handler-level tests for GET/POST/persist success and error paths - Add repoAPIOverride field to AgentConfigHandler for test injection
- Add nil guard to GetRepairModel to prevent panic when processor is uninitialized, mirroring existing GetResolverModel pattern - Protect Processor.model field with sync.RWMutex to prevent data race between concurrent SetModel/GetModel calls
settings (canopy-5f51) Address roborev finding #680: document that UpdateAgentSettings expects complete AgentSettings (not partial updates). The caller UpdateAgentConfig already merges partial PATCH-style updates before calling this function, so the empty-model-clears behavior is safe and intentional.
The dashboard git diff view was broken because patch data was dropped in handleAgentCommit(). The IPC layer correctly forwarded patch data but RuntimeState never extracted or stored it. Two changes in pkg/daemon/state.go: 1. Added Patch and Truncated fields to GitCommit struct 2. Extract and assign patch/truncated from payload in handleAgentCommit()
endpoint The Save button in RulesPanel was calling POST /rules/save which doesn't exist on the server. The server only has /rules/persist-all. The /save path fell through to the rule-name matcher treating 'save' as a rule name, returning 405 Method Not Allowed. Fixed by updating the client endpoint to match the server route.
fields Address roborev finding #690: adds tests verifying that handleAgentCommit preserves patch and truncated fields through runtime state, including the case where these fields are absent.
…46py) accordion Merge the separate BeadsPane (left) and RulesPanel (right) into a single left-side SidebarPanel with expandable accordion sections. This frees the right side entirely for the agent grid and provides a simpler layout. - Create SidebarPanel component with collapsible accordion sections - Add embedded rendering mode to BeadsPane and RulesPanel - On small screens (<=768px), sidebar renders as overlay with backdrop - Accordion open/closed state persisted per-section in localStorage - Single resize handle for the unified sidebar width
- Fix executor_test.go to match renamed ImplementorSystemPrompt - Remove unused vars in AgentConfigPanel.tsx and SidebarPanel.tsx - Add missing useCallback dependencies in useWebSocket.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/rules/persist-allendpointTest plan
go test ./...passesgo build ./...passes