.kilo/plans/agent-manager-multi-project-configuration.md
Status: Blocking architecture for multi-project release
Date: 2026-07-22
This document is the canonical configuration specification for Agent Manager multi-project support. The main UI/runtime plan references it and must not duplicate or contradict it.
The executable sequence from the current branch is agent-manager-multi-project-implementation-handoff.md.
Keep the useful current split between user and project settings, but make every Settings read and write target explicit, immutable, revisioned, and independent from Agent Manager activation.
Multi-project must not ship broadly while Settings can load a draft for project A and resolve its save target from the mutable active project B.
Kilo has four configuration stores:
| Store | Example | Owner |
|---|---|---|
| VS Code preferences | VS Code settings.json | This VS Code user/installation |
| Kilo user config | ~/.config/kilo/kilo.json | User defaults across projects and Kilo clients |
| Kilo project config | <repo>/.kilo/kilo.jsonc | Repository behavior and overrides |
| Runtime/session state | In-memory, directory-qualified | One project, worktree, or session |
The shared Settings save currently calls splitConfigByScope():
commit_message is written to project config;indexing.enabled is written to project config;The Indexing tab also has an explicit Global/Project selector and can write the entire indexing object to either layer. This is too broad because project config can receive provider/model/vector-store credentials and infrastructure settings.
Several controls are VS Code preferences and bypass Kilo config entirely, including autocomplete UI, browser automation, notifications, max auto-approve cost, commit-message output language, and indexing button visibility.
The current protocol does not bind a draft to the config target it was loaded from:
KiloProvider.fetchAndSendConfig() resolves a mutable current directory and sends unqualified configLoaded state.updateConfig.KiloProvider.handleUpdateConfig() resolves the current directory again at save time and may write A's draft to B.Reads are directory-scoped, but writes are not bound to the read target. This is the release blocker.
The backend also lacks an expected target/revision precondition, so external editors or another window can overwrite a config between read and save.
These remain in VS Code settings and do not participate in project config:
These are personal defaults or security policy and should be edited in User scope:
A trusted project may override many of these at runtime, but editing User scope never writes those overrides.
These describe repository behavior and are valid project settings:
Project configuration may override user model/agent/tool defaults, but provider credentials and security-policy weakening must not be silently authored into a repository.
Indexing enablement is privacy consent, not repository configuration. Store it outside the repository, keyed by canonical ProjectId in machine-local extension state:
Effective indexing requires both valid user-global indexing configuration and machine-local consent for that project.
| Settings tab | Correct editable target |
|---|---|
| Models | User by default; explicit Project scope may override models/agents |
| Providers | User only for credentials/endpoints; project-provided entries are source-labelled |
| Agent Behaviour | User or explicit trusted Project scope |
| Auto Approve | User Kilo config; max cost remains VS Code preference |
| Browser | VS Code preferences |
| Checkpoints | User default or explicit Project override |
| Display | User config |
| Autocomplete | VS Code preferences |
| Notifications | VS Code preferences |
| Context | User defaults; repository watcher/instruction rules in explicit Project scope |
| Commit Message | Project scope for prompt; language remains VS Code preference |
| Indexing | User for provider/model/credentials/storage; machine-local project consent for enablement; Project for repository rules |
| Experimental | User config; multi-project also mirrors to VS Code preference |
| Sandboxing | User config only |
| Language | VS Code preference |
| MCP/Commands/Skills | User defaults or explicit trusted Project scope |
No field silently chooses a file during save. The UI must display its scope.
Use explicit scope and project controls:
Scope: User | Project
Project: backend
Target: /projects/backend/.kilo/kilo.jsonc
Runtime config still follows the exact session directory. Settings Project scope targets the registered project root, not the active worktree. Worktree-config editing is a separate future feature requiring an explicit WorktreeRef.
A Settings read returns an opaque binding:
interface SettingsBinding {
id: string
connectionGeneration: number
scope: "global" | "project"
project?: {
projectId: string
root: string
generation: number
}
directory: string
target: {
scope: "global" | "project"
path: string
revision: string
exists: boolean
writable: boolean
}
}
The write contains only the opaque binding and patch:
interface WriteSettingsConfig {
type: "settingsConfig.write"
requestId: string
bindingId: string
set: Record<string, unknown>
unset: string[][]
}
The extension stores the authoritative binding. On write it must:
getWorkspaceDirectory(), contexts.active(), or use a worktree/session fallback;{ requestId, bindingId } response.Bindings expire after save, reconnect, trust revocation, project removal, or context generation change.
GET /config/overlay must return the exact global/project target path, parsed raw target config, effective config/source metadata, and a revision.
The revision is a SHA-256 fingerprint of canonical target path, existence marker, and exact file bytes. This catches content changes, JSONC comment-only edits, and target changes.
PATCH /config/overlay accepts one scope and requires:
{
scope: "global" | "project"
set: Record<string, unknown>
unset: string[][]
expected: {
path: string
revision: string
}
}
The backend must re-resolve the authoritative target, verify path/revision under a target lock, patch the raw target layer, validate it, atomically replace the file, and return a fresh snapshot. It never accepts an arbitrary client path.
Expected failures include expired binding, unknown/untrusted project, changed target, revision conflict, invalid config, non-writable target, and I/O failure. Every failure preserves the draft.
configLoaded/updateConfig with settings read/write messages carrying request and binding IDs.splitConfigByScope saves with explicit scope on every editable control.ProjectId, default off.ProjectRef, resolve the immutable registered root, and verify trust.indexing.enabled: true cannot grant indexing consent.commit_message.prompt and repository indexing rules still support explicit project writes.Keep multi-project disabled by default until the immutable binding/revision contract and the blocking tests above are implemented. The useful existing project-local behavior should be preserved, not removed; its write target must become explicit and immutable.