Skip to content

refactor(config): move Kilo config discovery behind a generic seam - #15282

Merged
marius-kilocode merged 18 commits into
v2from
config-discovery-seam
Oct 21, 2026
Merged

marius-kilocode merged 18 commits into
v2from
config-discovery-seam

Conversation

@marius-kilocode

@marius-kilocode marius-kilocode commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

What Problem This Solves

#15018, commit 4c51aabf0d8, put Kilo config discovery into upstream files. Core imported a Kilo module, and six upstream tests were changed to Kilo fixtures. These were registered as rung 3 but were feature patches by the host strategy.

The internal-release decision also requires isolation from v1: a kilo2 binary must read and write kilo2 config, not a shared project .kilo or kilo.json.

Why This Change Was Made

Upstream discovery gets a generic layout node with upstream defaults. The Kilo host supplies the internal-release values through its existing overrides.

Area Before After
Core discovery Kilo import, names, order and boundary in shared code Generic LayoutService with upstream defaults; no Kilo value, import or condition
Host No discovery replacement KiloHost.overrides replaces ConfigDiscovery.node with KiloConfigDiscovery.layout
Names and orders Shared code and CLI name lists One Kilo-owned source: packages/util/src/kilocode/config/files.ts
CLI config writers Inline Kilo lists Tagged call-out to that same source
Automatic-update policy Read config.json and opencode.json(c) Read kilo2.json(c), last defined policy wins
Six upstream config tests Kilo-specific edits Byte-identical to upstream v2.1.0 (9d90d892dcb)

The layout has names, directories, directoryFiles and walk. It retains upstream behavior without the host. A whole discovery-service replacement would copy the upstream walk, system/MDM handling and global-root filter; the layout avoids that mirror.

User Impact

Decision 12, updated 2026-10-21, is reflected in U-E1-07, U-E3-03, U-E3-12 and U-E8-02:

Internal v2 surface Identity
Built executable, help/version name and app identity kilo2
Global config ~/.config/kilo2/kilo2.json, kilo2.jsonc (platform/XDG equivalents apply)
Project config kilo2.json, kilo2.jsonc, .kilo2/
Config, data, state, cache and temporary roots kilo2, separate from v1
New file from CLI writers kilo2.json
Global server writer Last existing kilo2.json(c), else kilo2.jsonc

There is no automatic filename fallback. Explicit config-path overrides still accept any filename. Root/global files read JSON then JSONC; .kilo2 reads JSONC then JSON, preserving v1 file ordering. The worktree boundary and global root-only rule are retained.

Deliberate differences from v1:

v1 behavior Internal v2 behavior Follow-up
Read kilo.json(c) and .kilo Ignored; only kilo2 names Opt-in, non-destructive v1 import in U-E3-03
Read root opencode.json(c) and legacy .kilocode Ignored U-E3-03
Read ~/.kilocode and ~/.kilo home directories Not implemented, also absent before this PR unattributed:config:db17aa6942ce stays open
Shared runtime config names Explicit kilo2 to kilo rename/migration at release U-E3-03, U-E3-12; store consolidation still obeys D6/U-E8-02

The repository's .kilo/ contributor tooling is not renamed. The repo's unused upstream .opencode/ directory is deleted and registered in skipFiles, using the same process as #15151. New upstream additions under it stay removed.

This PR merges the latest shipping base, 0311551d521, including #15288. The alpha installer already installs as kilo2; the build, workflow smoke path and baseline runner now agree with it. The useful server-debugging instructions from the removed opencode-dev skill are retained in .kilo/skills/v2-migration.

Remaining work is not claimed complete: TUI .opencode themes/plugins, upstream-named system/MDM paths, Plan storage, Gateway project-ID files and updater distribution endpoints/install targets. These remain in the migration plan. Current config loading does not need a backend schema change; the existing schema endpoint proxies upstream's v2 schema plus Kilo extras.

Placement

Change Rung Why not lower Proof
Core discovery and directory file order 3 No prior discovery service; plugins load after discovery Generic layout in core/src/config/discovery.ts, consumed in config.ts
Kilo names, order and worktree bound 2 Host service replacement KiloConfigDiscovery.layout, KiloHost.overrides
CLI MCP/plugin config writes and updater policy 4 CLI cannot import Core or reach its layout node Calls into util/src/kilocode/config/files.ts; reasons in shared-patches registry
Internal binary identity 2 Host owns the build and app identity kilo-cli/script/build.ts, src/app.ts; upstream CLI defaults unchanged

Shared-patch delta from current origin/v2: 32 to 27 files, 288 to 204 marked lines, 39 unmarked lines unchanged, 0 deleted shared files. Core Kilo imports drop to zero in both discovery and config. Core-internal imports across Kilo adapters stay at 9.

Progress map

87 to 89 done cells; this internal-release follow-up does not change the count.

  • Host proofs close unattributed:config:546539fc1643 (global read order) and unattributed:config:8cdbdec70393 (global write preference), with the internal-release filenames selected by decision 12.
  • db17aa6942ce stays open for home directories. Project trust confinement (4d540bb826f4, eb34f040b986) and managed paths (73db79553730) are not claimed reproduced.
  • The layout interface and upstream test reverts reduce the shared conflict surface; the CLI policy patch remains honestly rung 4.

Evidence

Latest focused verification uses Bun 1.4.2:

  • Core config, Kilo discovery, formatter, plugin reload and session activation: 291 pass. All six upstream files remain byte-identical to the pin.
  • CLI Kilo config tests, updater tests/install tests and import boundaries: 53 pass, 5 existing platform skips. The new wrapper also verifies 7 passing child cases for updater policy.
  • Host tests, including app-directory isolation and the server layout override: 11 pass.
  • Updater regression exercises actual run(), not manual check() (which bypasses policy). A stable-build child avoids the local-build guard. Real scoped files test disabled JSON, JSONC notification override and all five ignored legacy filenames. No network/install occurs. Mutation checks fail for old filenames and reversed precedence; ten repeated runs pass.
  • Native kilo2 build and script/kilocode/smoke-host.ts: pass, including compiled version identity and isolated background-service startup/shutdown.
  • Typechecks: Core, Util and Kilo host pass. CLI and canonical bun run check are blocked by the existing Node-type errors (zstdCompressSync in AI; registerHooks in Plugin), not errors in changed files. Repository lint: 0 errors.
  • Shared-patch and annotation checkers pass. Progress output is checked on the PR merge ref because the history chart is first-parent based. The import-boundary check passes.

Real self-tests, all with temporary profiles and no changes to user configuration:

  • bun dev debug config: global kilo2.json(c), root kilo2.json and .kilo2/kilo2.json(c) load in the expected order. V1/OpenCode files, the config above the worktree root and nested .kilo2 inside the global directory are ignored.
  • Editing .kilo2/kilo2.json and root kilo2.json is reflected by the same service PID without restart.
  • bun dev mcp add: existing project .kilo2/kilo2.jsonc, global kilo2.jsonc and fresh kilo2.json are the write targets. Seeded v1 project/global config and database bytes remain unchanged.
  • Real TUI in the isolated VS Code terminal harness: /kilo2-only from .kilo2/command appears; /v1-only from .kilo/command reports No matching commands. No model request was needed.
  • Independent review found one stale baseline smoke path after the binary rename; it was corrected to bin/kilo2.

Every marked line in the discovery, config and CLI write files now calls out to Kilo-owned code (core/src/kilocode/config/discovery.ts, util/src/kilocode/config/files.ts) and carries kilocode_change(config). The files are registered as rung 4 with the reason. The CLI reads the names from util because it may not import Core. Kilo behavior tests move to the Kilo-owned test paths, the upstream test fixtures keep their Kilo edits until a generic seam lands. markedLines limit 316 to 245.
Upstream discovery reads a ConfigDiscovery layout node with upstream defaults. The Kilo host replaces it with KiloConfigDiscovery.layout, so the default graph behaves as upstream and the upstream config tests run unchanged. Kilo v2 reads only kilo.json, kilo.jsonc and .kilo (product decision, 2026-10-20).
Move the kilo_port proofs to the host override, keep an upstream fallback for the write target instead of a non-null assertion, harden the host test, cover the kilo.jsonc write preference, and scope the config-name decision in the plans.
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

➖ v2 migration progress: 0.9% → 0.9% (+2 done, 0 total, 0 dropped; 89/9829 cells)

Placement: 40 new marked lines in shared files (31 seam, 9 patch), 2 new kilo_port annotations, 1 new core-internal import (@opencode/core/config/discovery).

Ratchets: 9 core-internal imports (base 9); shared patches by rung 20 seam, 7 patch (base 29 seam, 3 patch).

Feature Before After Change
unattributed:config 0/130 2/130 +2 patches done
2 cells closed
  • patch:unattributed:config:546539fc1643
  • patch:unattributed:config:8cdbdec70393

v2 migration progress map at this commit

Full map · how cells close

@github-actions github-actions Bot added the area:cli CLI, TUI and core engine label Oct 20, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Incremental review of b6935ef..b51372ddb (the v2.1.0 base merge, the kilo2 identity commit e16bd61, and a progress refresh).

  • The kilo2 rename is consistent end to end: KiloConfigFiles is the single source for kilo2.json/kilo2.jsonc and .kilo2 in discovery, the host override, the CLI writers and the updater policy; KiloApp.name, the build-time app-directory define, the user agent, OPENCODE_CLI_NAME and the workflow smoke path (bin/kilo2) all agree, and smoke-host.ts asserts the matching version prefix.
  • The shared config seam keeps upstream behavior by construction: the default Layout (opencode.json, opencode.jsonc, .opencode, unbounded walk, last-existing global write target) matches upstream v2.1.0 (9d90d89) line for line, and the six restored upstream test files remain byte-identical.
  • The Kilo layout still has no v1 fallback: write-target.test.ts, discovery.test.ts and host.test.ts explicitly seed kilo.json(c), .kilo, .kilocode and opencode.* and assert they are ignored. The .kilo2 read order (.json wins) and the root-file order (.jsonc wins) are preserved and covered.
  • The previous SUGGESTION is resolved: updater-policy.test.ts now exercises Updater.run() in a release-build child and covers both kilo2.json/kilo2.jsonc precedence and all five ignored legacy filenames.
  • No new listener, timer, subscription, retained buffer or unbounded cache is introduced by the changed lines, so this increment adds no memory-leak surface.
  • No change to the .opencode/ deletion or its documentation was made in this increment; the earlier deletion review still stands.
Files Reviewed (24 files)
  • .github/workflows/kilo-v2.yml
  • migration-tracking/plans/kilo-v2-migration-plan.md
  • migration-tracking/plans/migration-context.md
  • migration-tracking/progress/README.md
  • migration-tracking/progress/history.svg
  • migration-tracking/progress/progress.json
  • migration-tracking/technical-notes/script/v2-baseline.ts
  • packages/cli/src/commands/handlers/mcp/add.ts
  • packages/cli/test/kilocode/config/updater-policy.test.ts
  • packages/cli/test/kilocode/config/write-target.test.ts
  • packages/core/src/kilocode/config/discovery.ts
  • packages/core/test/kilocode/config/discovery.test.ts
  • packages/kilo-cli/script/build.ts
  • packages/kilo-cli/src/app.ts
  • packages/kilo-cli/src/env.ts
  • packages/kilo-cli/src/host.ts
  • packages/kilo-cli/test/host.test.ts
  • packages/util/src/kilocode/config/files.ts
  • script/kilocode/smoke-host.ts
  • script/upstream/shared-patches.json
  • CONTRIBUTING.md, package.json, packages/kilo-cli/package.json, script/kilocode/install-alpha.ts (base-merge content, checked for contract consistency with the rename)
Previous Review Summaries (4 snapshots, latest commit b6935ef)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit b6935ef)

Status: 1 Issue Found | Recommendation: Address before merge

Incremental review of 0b0e79f..b6935ef. The branch merged origin/v2 (v2.1.0, 4de5f41) and added one commit (a68625ab70) that reads the autoupdate policy from Kilo config names.

  • The sync merge kept the PR's work. Relative to 0b0e79f, the only PR files that changed are updater.ts, shared-patches.json, utils/config.ts and the regenerated progress files; the discovery seam, the host overrides and the Kilo config modules are untouched.
  • The six restored upstream test files now match the v2.1.0 tree (9d90d89) byte for byte, so the merge dropped no upstream test change.
  • The new line is correct: KiloConfigFiles.names is ["kilo.json", "kilo.jsonc"] and findLast keeps .jsonc winning, the same order discovery and writeCandidates use.
  • The shared-patches.json raise (files 25 to 26, markedLines 195 to 197) comes with a rung-4 entry and a stated seam for updater.ts.
  • No new listener, timer or retained buffer is introduced, so the changed lines add no memory-leak surface.
  • PR body note: the table still names the upstream pin 775d1d0d713 for the six restored tests. After the sync they match v2.1.0 (9d90d89), so that pin reference should be refreshed.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/cli/src/services/updater.ts 166 readPolicy's Kilo file list has no test, so the autoupdate-from-kilo.json fix has no regression coverage

Fix these issues in Kilo Cloud

Files Reviewed (9 files)
  • packages/cli/src/services/updater.ts - 1 issue
  • script/upstream/shared-patches.json
  • script/upstream/utils/config.ts
  • migration-tracking/plans/kilo-v2-migration-plan.md (PR-held rows, vs current base)
  • packages/core/test/config/config.test.ts, packages/core/test/config/reload.test.ts, packages/core/test/config/command-subagent.test.ts, packages/core/test/formatter.test.ts, packages/core/test/plugin/supervisor-reload.test.ts, packages/core/test/session-activation.test.ts (merge verification only)

Previous review (commit 0b0e79f)

Incremental review of 6e5d97b..0b0e79f (2 commits, 1 changed file, one table row in the migration plan).

  • The corrected agent-loading finding matches the code. color is a native agent key (agentKeys in packages/core/src/config/plugin/agent.ts:32), so a markdown agent using it takes the native decode path (agent.ts:186, :203). The native schema requires #RRGGBB (packages/schema/src/config/agent.ts:9), so color: accent or info fails decode and the whole document returns undefined (agent.ts:204) instead of loading. Dropping the earlier mode: primary claim is right, since mode is valid on the native schema.
  • The config-schema-endpoint rewrite is consistent with this repo: packages/core/src/plugin/skill/opencode.md:26 states that https://opencode.ai/config.json accepts v2 fields plus deprecated v1 fields. The cloud apps/web/src/app/config.json/route.ts path is outside this repository, so that part could not be verified here.
  • The row no longer claims the problem is limited to global agents, which matches the code path: decoding is identical for project and global markdown agents.
  • No runtime code in this increment; no memory-leak or resource-lifetime impact.
Files Reviewed (1 file)
  • migration-tracking/plans/kilo-v2-migration-plan.md

Previous review (commit 6e5d97b)

Status: No Issues Found | Recommendation: Merge

Incremental review of bd8845b..6e5d97b (4 commits, 41 deletions, 5 modified files).

  • The deleted .opencode/ project config has no live consumer: repo config files, tsconfig, scripts, tests and workflows do not reference it, and script/duplicate-pr.ts:67 mentions the duplicate-pr agent only inside the commented-out TODO body.
  • .opencode/** is registered in skipFiles (script/upstream/utils/config.ts:137) and shouldSkip matches it while leaving .kilo/** and docs/.opencode/** alone.
  • The new server-debugging docs match the code: OPENCODE_LOG_LEVEL (packages/util/src/observability/logging.ts:165), opencode-local.log (logging.ts:59-62), SIGUSR1 heap and SIGPROF CPU output names and the 10s window (packages/cli/src/heap.ts, packages/cli/src/cpu-profile.ts), and --standalone/--print-logs in the CLI flags.
  • progress/README.md and progress/history.svg agree with each other (three points, 89/9829) and with the unchanged progress.json.

No memory-leak or resource-lifetime changes in this increment; it contains no runtime code.

Files Reviewed (46 files)
  • .kilo/skills/v2-migration/SKILL.md
  • migration-tracking/plans/migration-context.md
  • migration-tracking/progress/README.md
  • migration-tracking/progress/history.svg
  • script/upstream/utils/config.ts
  • .opencode/** (41 deleted files: agents, commands, skills, glossary, tools, themes, plugins, opencode.jsonc, tui.json, env.d.ts)

Previous review (commit bd8845b)

Status: No Issues Found | Recommendation: Merge

The Kilo config-discovery behavior is now a ConfigDiscovery.Layout service node with upstream defaults, and the Kilo host supplies KiloConfigDiscovery.layout through KiloHost.overrides. I verified the default graph still matches upstream (packages/core/src/config/discovery.ts and the Config.update write target are line-for-line equivalent to the pin at 775d1d0d713), that the six previously Kilo-edited upstream test files are byte-identical to that pin, and that .kilo read order, global root-only reads, the worktree boundary and the global write preference are covered by the new Kilo tests plus the host override test. No memory or watcher leaks found: the new host test builds its layer inside Effect.scoped with filewatcher: false and removes its temp root in finally, and the CLI write-target tests clean their temp roots in afterEach. Shared-file changes are limited to marked seams and the shared-patches.json limits went down (31/316 to 25/230).

Notes (not findings): managed config under the system directory now reads kilo.json/kilo.jsonc only, which follows recorded decision 11 but means an existing /etc/opencode/opencode.json managed file is no longer enforced; U-E1-07 already lists managed config as a follow-up, so this is expected and deliberate.

Files Reviewed (24 files)
  • migration-tracking/plans/kilo-v2-migration-plan.md
  • migration-tracking/plans/migration-context.md
  • migration-tracking/plans/v2-host-strategy.md
  • migration-tracking/progress/README.md
  • migration-tracking/progress/history.svg
  • migration-tracking/progress/progress.json
  • migration-tracking/progress/progress.svg
  • packages/cli/script/service-smoke.ts
  • packages/cli/src/commands/handlers/mcp/add.ts
  • packages/cli/src/commands/handlers/plugin/add.ts
  • packages/cli/src/commands/handlers/plugin/remove.ts
  • packages/cli/test/kilocode/config/write-target.test.ts
  • packages/core/src/config.ts
  • packages/core/src/config/discovery.ts
  • packages/core/src/kilocode/config/discovery.ts
  • packages/core/test/config/command-subagent.test.ts (byte-identical to upstream pin)
  • packages/core/test/config/config.test.ts (byte-identical to upstream pin)
  • packages/core/test/config/reload.test.ts (byte-identical to upstream pin)
  • packages/core/test/formatter.test.ts (byte-identical to upstream pin)
  • packages/core/test/kilocode/config/discovery.test.ts
  • packages/core/test/plugin/supervisor-reload.test.ts (byte-identical to upstream pin)
  • packages/core/test/session-activation.test.ts (byte-identical to upstream pin)
  • packages/kilo-cli/src/host.ts
  • packages/kilo-cli/test/host.test.ts
  • packages/util/src/kilocode/config/files.ts
  • script/upstream/shared-patches.json

Reviewed by deepseek-v4.1-flash-1017 · Input: 119.3K · Output: 46.1K · Cached: 4M

Review guidance: REVIEW.md from base branch v2

Kilo keeps its project config in .kilo. The sync skips upstream changes under .opencode (skipFiles), and the shared-patch counter does not count the removal.
CI checks the map on the PR merge commit, whose first-parent history has no intermediate progress.json from this branch. Use that output.
Port the parts of upstream's opencode-dev skill that apply to the Kilo host, with Kilo profile paths: service, API requests, logs, heap and CPU profiles, inspector. Each command was checked with bun dev in a temporary profile. Fix the log path of a test profile.
# Conflicts:
#	migration-tracking/plans/migration-context.md
#	script/upstream/shared-patches.json
…iscovery-seam

# Conflicts:
#	script/upstream/shared-patches.json
The CLI updater read config.json/opencode.json(c) in the Kilo global directory, so autoupdate in kilo.json was ignored. Read KiloConfigFiles.names instead, the same source the CLI write commands use.
Comment thread packages/cli/src/services/updater.ts
…iscovery-seam

# Conflicts:
#	migration-tracking/progress/README.md
#	migration-tracking/progress/progress.json
#	script/upstream/shared-patches.json
@marius-kilocode
marius-kilocode merged commit c596529 into v2 Oct 21, 2026
24 checks passed
@marius-kilocode
marius-kilocode deleted the config-discovery-seam branch October 21, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:cli CLI, TUI and core engine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants